ソースを参照

Merge pull request #102330 from RGDTAB/add_partial_smaa

Add SMAA 1x to screenspace AA options
Thaddeus Crews 2 ヶ月 前
コミット
e89c4b0f9f

+ 12 - 0
COPYRIGHT.txt

@@ -174,6 +174,18 @@ Comment: AccessKit
 Copyright: 2023, The AccessKit Authors.
 Copyright: 2023, The AccessKit Authors.
 License: Expat
 License: Expat
 
 
+Files: servers/rendering/renderer_rd/shaders/smaa_blending.glsl
+ servers/rendering/renderer_rd/shaders/smaa_weight_calculation.glsl
+ servers/rendering/renderer_rd/shaders/smaa_edge_detection.glsl
+ thirdparty/smaa/*
+Comment: Subpixel Morphological Antialiasing
+Copyright: 2013 Jorge Jimenez ([email protected])
+ 2013 Jose I. Echevarria ([email protected])
+ 2013 Belen Masia ([email protected])
+ 2013 Fernando Navarro ([email protected])
+ 2013 Diego Gutierrez ([email protected])
+License: Expat
+
 Files: thirdparty/amd-fsr/*
 Files: thirdparty/amd-fsr/*
 Comment: AMD FidelityFX Super Resolution
 Comment: AMD FidelityFX Super Resolution
 Copyright: 2021, Advanced Micro Devices, Inc.
 Copyright: 2021, Advanced Micro Devices, Inc.

+ 4 - 0
doc/classes/ProjectSettings.xml

@@ -2712,6 +2712,10 @@
 			[b]Note:[/b] Screen-space antialiasing is only supported in the Forward+ and Mobile rendering methods, not Compatibility.
 			[b]Note:[/b] Screen-space antialiasing is only supported in the Forward+ and Mobile rendering methods, not Compatibility.
 			[b]Note:[/b] This property is only read when the project starts. To set the screen-space antialiasing mode at runtime, set [member Viewport.screen_space_aa] on the root [Viewport] instead, or use [method RenderingServer.viewport_set_screen_space_aa].
 			[b]Note:[/b] This property is only read when the project starts. To set the screen-space antialiasing mode at runtime, set [member Viewport.screen_space_aa] on the root [Viewport] instead, or use [method RenderingServer.viewport_set_screen_space_aa].
 		</member>
 		</member>
+		<member name="rendering/anti_aliasing/quality/smaa_edge_detection_threshold" type="float" setter="" getter="" default="0.05">
+			Sets the sensitivity to edges when using SMAA for antialiasing. Lower values will catch more edges, at a potentially higher performance cost.
+			[b]Note:[/b] This property is only read when the project starts. There is currently no way to change this setting at run-time.
+		</member>
 		<member name="rendering/anti_aliasing/quality/use_debanding" type="bool" setter="" getter="" default="false">
 		<member name="rendering/anti_aliasing/quality/use_debanding" type="bool" setter="" getter="" default="false">
 			If [code]true[/code], uses a fast post-processing filter to make banding significantly less visible in 3D. 2D rendering is [i]not[/i] affected by debanding unless the [member Environment.background_mode] is [constant Environment.BG_CANVAS].
 			If [code]true[/code], uses a fast post-processing filter to make banding significantly less visible in 3D. 2D rendering is [i]not[/i] affected by debanding unless the [member Environment.background_mode] is [constant Environment.BG_CANVAS].
 			In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger.
 			In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger.

+ 4 - 1
doc/classes/RenderingServer.xml

@@ -5141,7 +5141,10 @@
 		<constant name="VIEWPORT_SCREEN_SPACE_AA_FXAA" value="1" enum="ViewportScreenSpaceAA">
 		<constant name="VIEWPORT_SCREEN_SPACE_AA_FXAA" value="1" enum="ViewportScreenSpaceAA">
 			Use fast approximate antialiasing. FXAA is a popular screen-space antialiasing method, which is fast but will make the image look blurry, especially at lower resolutions. It can still work relatively well at large resolutions such as 1440p and 4K.
 			Use fast approximate antialiasing. FXAA is a popular screen-space antialiasing method, which is fast but will make the image look blurry, especially at lower resolutions. It can still work relatively well at large resolutions such as 1440p and 4K.
 		</constant>
 		</constant>
-		<constant name="VIEWPORT_SCREEN_SPACE_AA_MAX" value="2" enum="ViewportScreenSpaceAA">
+		<constant name="VIEWPORT_SCREEN_SPACE_AA_SMAA" value="2" enum="ViewportScreenSpaceAA">
+			Use subpixel morphological antialiasing. SMAA may produce clearer results than FXAA, but at a slightly higher performance cost.
+		</constant>
+		<constant name="VIEWPORT_SCREEN_SPACE_AA_MAX" value="3" enum="ViewportScreenSpaceAA">
 			Represents the size of the [enum ViewportScreenSpaceAA] enum.
 			Represents the size of the [enum ViewportScreenSpaceAA] enum.
 		</constant>
 		</constant>
 		<constant name="VIEWPORT_OCCLUSION_BUILD_QUALITY_LOW" value="0" enum="ViewportOcclusionCullingBuildQuality">
 		<constant name="VIEWPORT_OCCLUSION_BUILD_QUALITY_LOW" value="0" enum="ViewportOcclusionCullingBuildQuality">

+ 4 - 1
doc/classes/Viewport.xml

@@ -598,7 +598,10 @@
 		<constant name="SCREEN_SPACE_AA_FXAA" value="1" enum="ScreenSpaceAA">
 		<constant name="SCREEN_SPACE_AA_FXAA" value="1" enum="ScreenSpaceAA">
 			Use fast approximate antialiasing. FXAA is a popular screen-space antialiasing method, which is fast but will make the image look blurry, especially at lower resolutions. It can still work relatively well at large resolutions such as 1440p and 4K.
 			Use fast approximate antialiasing. FXAA is a popular screen-space antialiasing method, which is fast but will make the image look blurry, especially at lower resolutions. It can still work relatively well at large resolutions such as 1440p and 4K.
 		</constant>
 		</constant>
-		<constant name="SCREEN_SPACE_AA_MAX" value="2" enum="ScreenSpaceAA">
+		<constant name="SCREEN_SPACE_AA_SMAA" value="2" enum="ScreenSpaceAA">
+			Use subpixel morphological antialiasing. SMAA may produce clearer results than FXAA, but at a slightly higher performance cost.
+		</constant>
+		<constant name="SCREEN_SPACE_AA_MAX" value="3" enum="ScreenSpaceAA">
 			Represents the size of the [enum ScreenSpaceAA] enum.
 			Represents the size of the [enum ScreenSpaceAA] enum.
 		</constant>
 		</constant>
 		<constant name="RENDER_INFO_OBJECTS_IN_FRAME" value="0" enum="RenderInfo">
 		<constant name="RENDER_INFO_OBJECTS_IN_FRAME" value="0" enum="RenderInfo">

+ 1 - 0
editor/editor_property_name_processor.cpp

@@ -274,6 +274,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
 	capitalize_string_remaps["sdk"] = "SDK";
 	capitalize_string_remaps["sdk"] = "SDK";
 	capitalize_string_remaps["sec"] = "(sec)"; // Unit.
 	capitalize_string_remaps["sec"] = "(sec)"; // Unit.
 	capitalize_string_remaps["signtool"] = "signtool";
 	capitalize_string_remaps["signtool"] = "signtool";
+	capitalize_string_remaps["smaa"] = "SMAA";
 	capitalize_string_remaps["sms"] = "SMS";
 	capitalize_string_remaps["sms"] = "SMS";
 	capitalize_string_remaps["srgb"] = "sRGB";
 	capitalize_string_remaps["srgb"] = "sRGB";
 	capitalize_string_remaps["ssao"] = "SSAO";
 	capitalize_string_remaps["ssao"] = "SSAO";

+ 1 - 0
pyproject.toml

@@ -89,4 +89,5 @@ ignore-words-list = [
 	"textin",
 	"textin",
 	"thirdparty",
 	"thirdparty",
 	"vai",
 	"vai",
+	"Masia",
 ]
 ]

+ 1 - 1
scene/main/scene_tree.cpp

@@ -2060,7 +2060,7 @@ SceneTree::SceneTree() {
 	const bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
 	const bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
 	root->set_use_hdr_2d(use_hdr_2d);
 	root->set_use_hdr_2d(use_hdr_2d);
 
 
-	const int ssaa_mode = GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), 0);
+	const int ssaa_mode = GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast),SMAA (Average)"), 0);
 	root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));
 	root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));
 
 
 	const bool use_taa = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/use_taa", false);
 	const bool use_taa = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/use_taa", false);

+ 2 - 1
scene/main/viewport.cpp

@@ -5127,7 +5127,7 @@ void Viewport::_bind_methods() {
 	ADD_GROUP("Rendering", "");
 	ADD_GROUP("Rendering", "");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_2d", "get_msaa_2d");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_2d", "get_msaa_2d");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_3d", "get_msaa_3d");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_3d", "get_msaa_3d");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast),SMAA (Average)"), "set_screen_space_aa", "get_screen_space_aa");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_taa"), "set_use_taa", "is_using_taa");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_taa"), "set_use_taa", "is_using_taa");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
@@ -5216,6 +5216,7 @@ void Viewport::_bind_methods() {
 
 
 	BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_DISABLED);
 	BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_DISABLED);
 	BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_FXAA);
 	BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_FXAA);
+	BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_SMAA);
 	BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_MAX);
 	BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_MAX);
 
 
 	BIND_ENUM_CONSTANT(RENDER_INFO_OBJECTS_IN_FRAME);
 	BIND_ENUM_CONSTANT(RENDER_INFO_OBJECTS_IN_FRAME);

+ 1 - 0
scene/main/viewport.h

@@ -136,6 +136,7 @@ public:
 	enum ScreenSpaceAA {
 	enum ScreenSpaceAA {
 		SCREEN_SPACE_AA_DISABLED,
 		SCREEN_SPACE_AA_DISABLED,
 		SCREEN_SPACE_AA_FXAA,
 		SCREEN_SPACE_AA_FXAA,
+		SCREEN_SPACE_AA_SMAA,
 		SCREEN_SPACE_AA_MAX
 		SCREEN_SPACE_AA_MAX
 	};
 	};
 
 

+ 40 - 0
servers/rendering/renderer_rd/effects/SCsub

@@ -1,6 +1,8 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 from misc.utility.scons_hints import *
 from misc.utility.scons_hints import *
 
 
+import methods
+
 Import("env")
 Import("env")
 
 
 env_effects = env.Clone()
 env_effects = env.Clone()
@@ -15,6 +17,44 @@ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
 
 
 env_effects.Prepend(CPPEXTPATH=[thirdparty_dir])
 env_effects.Prepend(CPPEXTPATH=[thirdparty_dir])
 
 
+
+def areatex_builder(target, source, env):
+    buffer = methods.get_buffer(str(source[0]))
+
+    with methods.generated_wrapper(str(target[0])) as file:
+        file.write(f"""\
+#define AREATEX_WIDTH 160
+#define AREATEX_HEIGHT 560
+#define AREATEX_PITCH (AREATEX_WIDTH * 2)
+#define AREATEX_SIZE (AREATEX_HEIGHT * AREATEX_PITCH)
+
+inline constexpr const unsigned char area_tex_png[] = {{
+    {methods.format_buffer(buffer, 1)}
+}};
+""")
+
+
+env.CommandNoCache("smaa_area_tex.gen.h", "#thirdparty/smaa/AreaTex.png", env.Run(areatex_builder))
+
+
+def searchtex_builder(target, source, env):
+    buffer = methods.get_buffer(str(source[0]))
+
+    with methods.generated_wrapper(str(target[0])) as file:
+        file.write(f"""\
+#define SEARCHTEX_WIDTH 64
+#define SEARCHTEX_HEIGHT 16
+#define SEARCHTEX_PITCH SEARCHTEX_WIDTH
+#define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH)
+
+inline constexpr const unsigned char search_tex_png[] = {{
+    {methods.format_buffer(buffer, 1)}
+}};
+""")
+
+
+env.CommandNoCache("smaa_search_tex.gen.h", "#thirdparty/smaa/SearchTex.png", env.Run(searchtex_builder))
+
 # This flag doesn't actually control anything GCC specific in FSR2. It determines
 # This flag doesn't actually control anything GCC specific in FSR2. It determines
 # if symbols should be exported, which is not required for Godot.
 # if symbols should be exported, which is not required for Godot.
 env_effects.Append(CPPDEFINES=["FFX_GCC"])
 env_effects.Append(CPPDEFINES=["FFX_GCC"])

+ 268 - 0
servers/rendering/renderer_rd/effects/smaa.cpp

@@ -0,0 +1,268 @@
+/**************************************************************************/
+/*  smaa.cpp                                                              */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "smaa.h"
+
+#include "core/config/project_settings.h"
+#include "core/io/image_loader.h"
+#include "servers/rendering/renderer_rd/effects/smaa_area_tex.gen.h"
+#include "servers/rendering/renderer_rd/effects/smaa_search_tex.gen.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
+#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+SMAA::SMAA() {
+	{
+		// Initialize edge detection.
+		Vector<String> smaa_modes;
+		smaa_modes.push_back("\n");
+		smaa.edge_shader.initialize(smaa_modes);
+
+		smaa.edge_shader_version = smaa.edge_shader.version_create();
+
+		RD::PipelineDepthStencilState stencil_state = RD::PipelineDepthStencilState();
+		stencil_state.enable_stencil = true;
+		stencil_state.back_op.reference = 0xff;
+		stencil_state.back_op.write_mask = 0xff;
+		stencil_state.back_op.compare_mask = 0xff;
+		stencil_state.back_op.pass = RD::STENCIL_OP_REPLACE;
+		stencil_state.front_op.reference = 0xff;
+		stencil_state.front_op.write_mask = 0xff;
+		stencil_state.front_op.compare_mask = 0xff;
+		stencil_state.front_op.pass = RD::STENCIL_OP_REPLACE;
+
+		for (int i = SMAA_EDGE_DETECTION_COLOR; i <= SMAA_EDGE_DETECTION_COLOR; i++) {
+			smaa.pipelines[i].setup(smaa.edge_shader.version_get_shader(smaa.edge_shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
+		}
+
+		edge_detection_threshold = GLOBAL_GET("rendering/anti_aliasing/quality/smaa_edge_detection_threshold");
+	}
+
+	{
+		// Initialize weight calculation.
+		Vector<String> smaa_modes;
+		smaa_modes.push_back("\n");
+		smaa.weight_shader.initialize(smaa_modes);
+
+		smaa.weight_shader_version = smaa.weight_shader.version_create();
+
+		RD::PipelineDepthStencilState stencil_state;
+		stencil_state.enable_stencil = true;
+		stencil_state.back_op.reference = 0xff;
+		stencil_state.back_op.compare_mask = 0xff;
+		stencil_state.back_op.compare = RD::COMPARE_OP_EQUAL;
+		stencil_state.front_op.reference = 0xff;
+		stencil_state.front_op.compare_mask = 0xff;
+		stencil_state.front_op.compare = RD::COMPARE_OP_EQUAL;
+
+		for (int i = SMAA_WEIGHT_FULL; i <= SMAA_WEIGHT_FULL; i++) {
+			smaa.pipelines[i].setup(smaa.weight_shader.version_get_shader(smaa.weight_shader_version, i - SMAA_WEIGHT_FULL), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
+		}
+	}
+
+	{
+		// Initialize color blending.
+		Vector<String> smaa_modes;
+		smaa_modes.push_back("\n");
+		smaa.blend_shader.initialize(smaa_modes);
+
+		smaa.blend_shader_version = smaa.blend_shader.version_create();
+
+		for (int i = SMAA_BLENDING; i <= SMAA_BLENDING; i++) {
+			smaa.pipelines[i].setup(smaa.blend_shader.version_get_shader(smaa.blend_shader_version, i - SMAA_BLENDING), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+		}
+	}
+
+	{
+		// Initialize SearchTex.
+		RD::TextureFormat tf;
+		tf.format = RD::DATA_FORMAT_R8_UNORM;
+		tf.width = SEARCHTEX_WIDTH;
+		tf.height = SEARCHTEX_HEIGHT;
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
+
+		smaa.search_tex = RD::get_singleton()->texture_create(tf, RD::TextureView(), Vector<Vector<unsigned char>>{ Image(search_tex_png).get_data() });
+	}
+
+	{
+		// Initialize AreaTex.
+		RD::TextureFormat tf;
+		tf.format = RD::DATA_FORMAT_R8G8_UNORM;
+		tf.width = AREATEX_WIDTH;
+		tf.height = AREATEX_HEIGHT;
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
+
+		smaa.area_tex = RD::get_singleton()->texture_create(tf, RD::TextureView(), Vector<Vector<unsigned char>>{ Image(area_tex_png).get_data() });
+	}
+
+	{
+		// Find smallest stencil texture format.
+		if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D16_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
+			smaa.stencil_format = RD::DATA_FORMAT_D16_UNORM_S8_UINT;
+		} else if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
+			smaa.stencil_format = RD::DATA_FORMAT_D24_UNORM_S8_UINT;
+		} else {
+			smaa.stencil_format = RD::DATA_FORMAT_D32_SFLOAT_S8_UINT;
+		}
+	}
+}
+
+SMAA::~SMAA() {
+	RD::get_singleton()->free(smaa.search_tex);
+	RD::get_singleton()->free(smaa.area_tex);
+
+	smaa.edge_shader.version_free(smaa.edge_shader_version);
+	smaa.weight_shader.version_free(smaa.weight_shader_version);
+	smaa.blend_shader.version_free(smaa.blend_shader_version);
+}
+
+void SMAA::allocate_render_targets(Ref<RenderSceneBuffersRD> p_render_buffers) {
+	Size2i full_size = p_render_buffers->get_internal_size();
+
+	// As we're not clearing these, and render buffers will return the cached texture if it already exists,
+	// we don't first check has_texture here.
+
+	p_render_buffers->create_texture(RB_SCOPE_SMAA, RB_EDGES, RD::DATA_FORMAT_R8G8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, full_size, 1, 1, true, true);
+	p_render_buffers->create_texture(RB_SCOPE_SMAA, RB_BLEND, RD::DATA_FORMAT_R8G8B8A8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, full_size, 1, 1, true, true);
+	p_render_buffers->create_texture(RB_SCOPE_SMAA, RB_STENCIL, smaa.stencil_format, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, full_size, 1, 1, true, true);
+}
+
+void SMAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer) {
+	UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+	ERR_FAIL_NULL(uniform_set_cache);
+	MaterialStorage *material_storage = MaterialStorage::get_singleton();
+	ERR_FAIL_NULL(material_storage);
+
+	memset(&smaa.edge_push_constant, 0, sizeof(SMAAEdgePushConstant));
+	memset(&smaa.weight_push_constant, 0, sizeof(SMAAWeightPushConstant));
+	memset(&smaa.blend_push_constant, 0, sizeof(SMAABlendPushConstant));
+
+	Size2i size = p_render_buffers->get_internal_size();
+	Size2 inv_size = Size2(1.0f / (float)size.x, 1.0f / (float)size.y);
+
+	smaa.edge_push_constant.inv_size[0] = inv_size.x;
+	smaa.edge_push_constant.inv_size[1] = inv_size.y;
+	smaa.edge_push_constant.threshold = edge_detection_threshold;
+
+	smaa.weight_push_constant.inv_size[0] = inv_size.x;
+	smaa.weight_push_constant.inv_size[1] = inv_size.y;
+	smaa.weight_push_constant.size[0] = size.x;
+	smaa.weight_push_constant.size[1] = size.y;
+
+	smaa.blend_push_constant.inv_size[0] = inv_size.x;
+	smaa.blend_push_constant.inv_size[1] = inv_size.y;
+
+	RID linear_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+	allocate_render_targets(p_render_buffers);
+	RID edges_tex = p_render_buffers->get_texture(RB_SCOPE_SMAA, RB_EDGES);
+	RID blend_tex = p_render_buffers->get_texture(RB_SCOPE_SMAA, RB_BLEND);
+	RID stencil_buffer = p_render_buffers->get_texture(RB_SCOPE_SMAA, RB_STENCIL);
+
+	RID edges_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(edges_tex, stencil_buffer);
+	RID blend_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(blend_tex, stencil_buffer);
+
+	RD::Uniform u_source_color;
+	u_source_color.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+	u_source_color.binding = 0;
+	u_source_color.append_id(linear_sampler);
+	u_source_color.append_id(p_source_color);
+
+	RD::Uniform u_edges_texture;
+	u_edges_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+	u_edges_texture.binding = 0;
+	u_edges_texture.append_id(linear_sampler);
+	u_edges_texture.append_id(edges_tex);
+
+	RD::Uniform u_area_texture;
+	u_area_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+	u_area_texture.binding = 0;
+	u_area_texture.append_id(linear_sampler);
+	u_area_texture.append_id(smaa.area_tex);
+
+	RD::Uniform u_search_texture;
+	u_search_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+	u_search_texture.binding = 1;
+	u_search_texture.append_id(linear_sampler);
+	u_search_texture.append_id(smaa.search_tex);
+
+	RD::Uniform u_blend_texture;
+	u_blend_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+	u_blend_texture.binding = 0;
+	u_blend_texture.append_id(linear_sampler);
+	u_blend_texture.append_id(blend_tex);
+
+	{
+		int mode = SMAA_EDGE_DETECTION_COLOR;
+		RID shader = smaa.edge_shader.version_get_shader(smaa.edge_shader_version, mode);
+		ERR_FAIL_COND(shader.is_null());
+
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(edges_framebuffer, RD::DRAW_CLEAR_COLOR_0 | RD::DRAW_CLEAR_STENCIL, Vector<Color>({ Color(0, 0, 0, 0) }), 1.0f, 0);
+		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, smaa.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(edges_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
+
+		RD::get_singleton()->draw_list_set_push_constant(draw_list, &smaa.edge_push_constant, sizeof(SMAAEdgePushConstant));
+		RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
+		RD::get_singleton()->draw_list_end();
+	}
+
+	{
+		int mode = SMAA_WEIGHT_FULL;
+		RID shader = smaa.weight_shader.version_get_shader(smaa.weight_shader_version, mode - SMAA_WEIGHT_FULL);
+		ERR_FAIL_COND(shader.is_null());
+
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(blend_framebuffer, RD::DRAW_CLEAR_COLOR_0, Vector<Color>({ Color(0, 0, 0, 0) }));
+		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, smaa.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(blend_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_edges_texture), 0);
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_area_texture, u_search_texture), 1);
+
+		RD::get_singleton()->draw_list_set_push_constant(draw_list, &smaa.weight_push_constant, sizeof(SMAAWeightPushConstant));
+		RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
+		RD::get_singleton()->draw_list_end();
+	}
+
+	{
+		int mode = SMAA_BLENDING;
+		RID shader = smaa.blend_shader.version_get_shader(smaa.blend_shader_version, mode - SMAA_BLENDING);
+		ERR_FAIL_COND(shader.is_null());
+
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::DRAW_IGNORE_COLOR_0);
+		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, smaa.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_blend_texture), 1);
+
+		RD::get_singleton()->draw_list_set_push_constant(draw_list, &smaa.blend_push_constant, sizeof(SMAABlendPushConstant));
+		RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
+		RD::get_singleton()->draw_list_end();
+	}
+}

+ 108 - 0
servers/rendering/renderer_rd/effects/smaa.h

@@ -0,0 +1,108 @@
+/**************************************************************************/
+/*  smaa.h                                                                */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/smaa_blending.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/smaa_edge_detection.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/smaa_weight_calculation.glsl.gen.h"
+#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
+#include "servers/rendering/renderer_scene_render.h"
+
+#include "servers/rendering_server.h"
+
+#define RB_SCOPE_SMAA SNAME("rb_smaa")
+
+#define RB_EDGES SNAME("edges")
+#define RB_BLEND SNAME("blend")
+#define RB_STENCIL SNAME("stencil")
+
+namespace RendererRD {
+
+class SMAA {
+private:
+	enum SMAAMode {
+		SMAA_EDGE_DETECTION_COLOR,
+		SMAA_WEIGHT_FULL,
+		SMAA_BLENDING,
+		SMAA_MAX,
+	};
+
+	struct SMAAEdgePushConstant {
+		float inv_size[2];
+		float threshold;
+		float reserved;
+	};
+
+	struct SMAAWeightPushConstant {
+		float inv_size[2];
+		uint32_t size[2];
+
+		float subsample_indices[4];
+	};
+
+	struct SMAABlendPushConstant {
+		float inv_size[2];
+		float reserved[2];
+	};
+
+	struct SMAAEffect {
+		SMAAEdgePushConstant edge_push_constant;
+		SmaaEdgeDetectionShaderRD edge_shader;
+		RID edge_shader_version;
+
+		SMAAWeightPushConstant weight_push_constant;
+		SmaaWeightCalculationShaderRD weight_shader;
+		RID weight_shader_version;
+
+		SMAABlendPushConstant blend_push_constant;
+		SmaaBlendingShaderRD blend_shader;
+		RID blend_shader_version;
+
+		RID search_tex;
+		RID area_tex;
+
+		RD::DataFormat stencil_format;
+
+		PipelineCacheRD pipelines[SMAA_MAX];
+	} smaa;
+
+	float edge_detection_threshold;
+
+public:
+	SMAA();
+	~SMAA();
+
+	void allocate_render_targets(Ref<RenderSceneBuffersRD> p_render_buffers);
+	void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer);
+};
+
+} // namespace RendererRD

+ 2 - 2
servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp

@@ -886,8 +886,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
 			using_subpass_post_process = false;
 			using_subpass_post_process = false;
 		}
 		}
 
 
-		if (rb->get_screen_space_aa() == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) {
-			// Can't do blit subpass because we're using FXAA.
+		if (rb->get_screen_space_aa() != RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED) {
+			// Can't do blit subpass because we're using screen space AA.
 			using_subpass_post_process = false;
 			using_subpass_post_process = false;
 		}
 		}
 
 

+ 57 - 3
servers/rendering/renderer_rd/renderer_scene_render_rd.cpp

@@ -470,6 +470,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		}
 		}
 	}
 	}
 
 
+	bool use_smaa = smaa && rb->get_screen_space_aa() == RS::VIEWPORT_SCREEN_SPACE_AA_SMAA;
+
 	RID render_target = rb->get_render_target();
 	RID render_target = rb->get_render_target();
 	RID color_texture = use_upscaled_texture ? rb->get_upscaled_texture() : rb->get_internal_texture();
 	RID color_texture = use_upscaled_texture ? rb->get_upscaled_texture() : rb->get_internal_texture();
 	Size2i color_size = use_upscaled_texture ? target_size : rb->get_internal_size();
 	Size2i color_size = use_upscaled_texture ? target_size : rb->get_internal_size();
@@ -682,8 +684,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target);
 		tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target);
 
 
 		RID dest_fb;
 		RID dest_fb;
-		if (spatial_upscaler != nullptr) {
-			// If we use a spatial upscaler to upscale we need to write our result into an intermediate buffer.
+		if (spatial_upscaler != nullptr || use_smaa) {
+			// If we use a spatial upscaler to upscale or SMAA to antialias we need to write our result into an intermediate buffer.
 			// Note that this is cached so we only create the texture the first time.
 			// Note that this is cached so we only create the texture the first time.
 			RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
 			RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
 			dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
 			dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
@@ -705,13 +707,61 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
 		RD::get_singleton()->draw_command_end_label();
 		RD::get_singleton()->draw_command_end_label();
 	}
 	}
 
 
+	if (use_smaa) {
+		RENDER_TIMESTAMP("SMAA");
+		RD::get_singleton()->draw_command_begin_label("SMAA");
+
+		RID dest_fb;
+		if (spatial_upscaler) {
+			rb->create_texture(SNAME("SMAA"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
+		}
+		if (rb->get_view_count() > 1) {
+			for (uint32_t v = 0; v < rb->get_view_count(); v++) {
+				RID source_texture = rb->get_texture_slice(SNAME("Tonemapper"), SNAME("destination"), v, 0);
+
+				RID dest_texture;
+				if (spatial_upscaler) {
+					dest_texture = rb->get_texture_slice(SNAME("SMAA"), SNAME("destination"), v, 0);
+				} else {
+					dest_texture = texture_storage->render_target_get_rd_texture_slice(render_target, v);
+				}
+				dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
+
+				smaa->process(rb, source_texture, dest_fb);
+			}
+		} else {
+			RID source_texture = rb->get_texture(SNAME("Tonemapper"), SNAME("destination"));
+
+			if (spatial_upscaler) {
+				RID dest_texture = rb->create_texture(SNAME("SMAA"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true);
+				dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture);
+			} else {
+				if (dest_is_msaa_2d) {
+					dest_fb = FramebufferCacheRD::get_singleton()->get_cache(texture_storage->render_target_get_rd_texture_msaa(render_target));
+					texture_storage->render_target_set_msaa_needs_resolve(render_target, true); // Make sure this gets resolved.
+				} else {
+					dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
+				}
+			}
+
+			smaa->process(rb, source_texture, dest_fb);
+		}
+
+		RD::get_singleton()->draw_command_end_label();
+	}
+
 	if (rb.is_valid() && spatial_upscaler) {
 	if (rb.is_valid() && spatial_upscaler) {
 		spatial_upscaler->ensure_context(rb);
 		spatial_upscaler->ensure_context(rb);
 
 
 		RD::get_singleton()->draw_command_begin_label(spatial_upscaler->get_label());
 		RD::get_singleton()->draw_command_begin_label(spatial_upscaler->get_label());
 
 
 		for (uint32_t v = 0; v < rb->get_view_count(); v++) {
 		for (uint32_t v = 0; v < rb->get_view_count(); v++) {
-			RID source_texture = rb->get_texture_slice(SNAME("Tonemapper"), SNAME("destination"), v, 0);
+			RID source_texture;
+			if (use_smaa) {
+				source_texture = rb->get_texture_slice(SNAME("SMAA"), SNAME("destination"), v, 0);
+			} else {
+				source_texture = rb->get_texture_slice(SNAME("Tonemapper"), SNAME("destination"), v, 0);
+			}
 			RID dest_texture = texture_storage->render_target_get_rd_texture_slice(render_target, v);
 			RID dest_texture = texture_storage->render_target_get_rd_texture_slice(render_target, v);
 
 
 			spatial_upscaler->process(rb, source_texture, dest_texture);
 			spatial_upscaler->process(rb, source_texture, dest_texture);
@@ -1553,6 +1603,7 @@ void RendererSceneRenderRD::init() {
 	copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage));
 	copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage));
 	debug_effects = memnew(RendererRD::DebugEffects);
 	debug_effects = memnew(RendererRD::DebugEffects);
 	luminance = memnew(RendererRD::Luminance(!can_use_storage));
 	luminance = memnew(RendererRD::Luminance(!can_use_storage));
+	smaa = memnew(RendererRD::SMAA);
 	tone_mapper = memnew(RendererRD::ToneMapper);
 	tone_mapper = memnew(RendererRD::ToneMapper);
 	if (can_use_vrs) {
 	if (can_use_vrs) {
 		vrs = memnew(RendererRD::VRS);
 		vrs = memnew(RendererRD::VRS);
@@ -1582,6 +1633,9 @@ RendererSceneRenderRD::~RendererSceneRenderRD() {
 	if (luminance) {
 	if (luminance) {
 		memdelete(luminance);
 		memdelete(luminance);
 	}
 	}
+	if (smaa) {
+		memdelete(smaa);
+	}
 	if (tone_mapper) {
 	if (tone_mapper) {
 		memdelete(tone_mapper);
 		memdelete(tone_mapper);
 	}
 	}

+ 2 - 0
servers/rendering/renderer_rd/renderer_scene_render_rd.h

@@ -39,6 +39,7 @@
 #ifdef METAL_ENABLED
 #ifdef METAL_ENABLED
 #include "servers/rendering/renderer_rd/effects/metal_fx.h"
 #include "servers/rendering/renderer_rd/effects/metal_fx.h"
 #endif
 #endif
+#include "servers/rendering/renderer_rd/effects/smaa.h"
 #include "servers/rendering/renderer_rd/effects/tone_mapper.h"
 #include "servers/rendering/renderer_rd/effects/tone_mapper.h"
 #include "servers/rendering/renderer_rd/effects/vrs.h"
 #include "servers/rendering/renderer_rd/effects/vrs.h"
 #include "servers/rendering/renderer_rd/environment/gi.h"
 #include "servers/rendering/renderer_rd/environment/gi.h"
@@ -60,6 +61,7 @@ protected:
 	RendererRD::CopyEffects *copy_effects = nullptr;
 	RendererRD::CopyEffects *copy_effects = nullptr;
 	RendererRD::DebugEffects *debug_effects = nullptr;
 	RendererRD::DebugEffects *debug_effects = nullptr;
 	RendererRD::Luminance *luminance = nullptr;
 	RendererRD::Luminance *luminance = nullptr;
+	RendererRD::SMAA *smaa = nullptr;
 	RendererRD::ToneMapper *tone_mapper = nullptr;
 	RendererRD::ToneMapper *tone_mapper = nullptr;
 	RendererRD::FSR *fsr = nullptr;
 	RendererRD::FSR *fsr = nullptr;
 	RendererRD::VRS *vrs = nullptr;
 	RendererRD::VRS *vrs = nullptr;

+ 123 - 0
servers/rendering/renderer_rd/shaders/effects/smaa_blending.glsl

@@ -0,0 +1,123 @@
+/**
+ * Copyright (C) 2013 Jorge Jimenez ([email protected])
+ * Copyright (C) 2013 Jose I. Echevarria ([email protected])
+ * Copyright (C) 2013 Belen Masia ([email protected])
+ * Copyright (C) 2013 Fernando Navarro ([email protected])
+ * Copyright (C) 2013 Diego Gutierrez ([email protected])
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software. As clarification, there
+ * is no requirement that the copyright notice and permission be included in
+ * binary distributions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#[vertex]
+#version 450
+
+layout(location = 0) out vec2 tex_coord;
+layout(location = 1) out vec4 offset;
+
+layout(push_constant, std430) uniform Params {
+	vec2 inv_size;
+	vec2 reserved;
+}
+params;
+
+void main() {
+	vec2 vertex_base;
+	if (gl_VertexIndex == 0) {
+		vertex_base = vec2(-1.0, -1.0);
+	} else if (gl_VertexIndex == 1) {
+		vertex_base = vec2(-1.0, 3.0);
+	} else {
+		vertex_base = vec2(3.0, -1.0);
+	}
+	gl_Position = vec4(vertex_base, 0.0, 1.0);
+	tex_coord = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
+	offset = fma(params.inv_size.xyxy, vec4(1.0, 0.0, 0.0, 1.0), tex_coord.xyxy);
+}
+
+#[fragment]
+#version 450
+
+layout(location = 0) in vec2 tex_coord;
+layout(location = 1) in vec4 offset;
+layout(set = 0, binding = 0) uniform sampler2D color_tex;
+layout(set = 1, binding = 0) uniform sampler2D blend_tex;
+
+layout(location = 0) out vec4 out_color;
+
+layout(push_constant, std430) uniform Params {
+	vec2 inv_size;
+	vec2 reserved;
+}
+params;
+
+#define textureLinear(tex, uv) srgb_to_linear(textureLod(tex, uv, 0.0).rgb)
+
+vec3 linear_to_srgb(vec3 color) {
+	// If going to srgb, clamp from 0 to 1.
+	color = clamp(color, vec3(0.0), vec3(1.0));
+	const vec3 a = vec3(0.055f);
+	return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
+vec3 srgb_to_linear(vec3 color) {
+	return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
+}
+
+void SMAAMovc(bvec2 cond, inout vec2 variable, vec2 value) {
+	if (cond.x) {
+		variable.x = value.x;
+	}
+	if (cond.y) {
+		variable.y = value.y;
+	}
+}
+
+void SMAAMovc(bvec4 cond, inout vec4 variable, vec4 value) {
+	SMAAMovc(cond.xy, variable.xy, value.xy);
+	SMAAMovc(cond.zw, variable.zw, value.zw);
+}
+
+void main() {
+	vec4 a;
+	a.x = texture(blend_tex, offset.xy).a;
+	a.y = texture(blend_tex, offset.zw).g;
+	a.wz = texture(blend_tex, tex_coord).xz;
+
+	if (dot(a, vec4(1.0, 1.0, 1.0, 1.0)) < 1e-5) {
+		out_color = textureLod(color_tex, tex_coord, 0.0);
+	} else {
+		bool h = max(a.x, a.z) > max(a.y, a.w);
+
+		vec4 blending_offset = vec4(0.0, a.y, 0.0, a.w);
+		vec2 blending_weight = a.yw;
+
+		SMAAMovc(bvec4(h, h, h, h), blending_offset, vec4(a.x, 0.0, a.z, 0.0));
+		SMAAMovc(bvec2(h, h), blending_weight, a.xz);
+		blending_weight /= dot(blending_weight, vec2(1.0, 1.0));
+
+		vec4 blending_coord = fma(blending_offset, vec4(params.inv_size.xy, -params.inv_size.xy), tex_coord.xyxy);
+
+		out_color.rgb = blending_weight.x * textureLinear(color_tex, blending_coord.xy);
+		out_color.rgb += blending_weight.y * textureLinear(color_tex, blending_coord.zw);
+		out_color.rgb = linear_to_srgb(out_color.rgb);
+		out_color.a = texture(color_tex, tex_coord).a;
+	}
+}

+ 120 - 0
servers/rendering/renderer_rd/shaders/effects/smaa_edge_detection.glsl

@@ -0,0 +1,120 @@
+/**
+ * Copyright (C) 2013 Jorge Jimenez ([email protected])
+ * Copyright (C) 2013 Jose I. Echevarria ([email protected])
+ * Copyright (C) 2013 Belen Masia ([email protected])
+ * Copyright (C) 2013 Fernando Navarro ([email protected])
+ * Copyright (C) 2013 Diego Gutierrez ([email protected])
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software. As clarification, there
+ * is no requirement that the copyright notice and permission be included in
+ * binary distributions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#[vertex]
+#version 450
+
+layout(location = 0) out vec2 tex_coord;
+layout(location = 1) out vec4 offset[3];
+
+layout(push_constant, std430) uniform Params {
+	vec2 inv_size;
+	float threshold;
+	float reserved;
+}
+params;
+
+void main() {
+	vec2 vertex_base;
+	if (gl_VertexIndex == 0) {
+		vertex_base = vec2(-1.0, -1.0);
+	} else if (gl_VertexIndex == 1) {
+		vertex_base = vec2(-1.0, 3.0);
+	} else {
+		vertex_base = vec2(3.0, -1.0);
+	}
+	gl_Position = vec4(vertex_base, 0.0, 1.0);
+	tex_coord = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
+
+	offset[0] = fma(params.inv_size.xyxy, vec4(-1.0, 0.0, 0.0, -1.0), tex_coord.xyxy);
+	offset[1] = fma(params.inv_size.xyxy, vec4(1.0, 0.0, 0.0, 1.0), tex_coord.xyxy);
+	offset[2] = fma(params.inv_size.xyxy, vec4(-2.0, 0.0, 0.0, -2.0), tex_coord.xyxy);
+}
+
+#[fragment]
+#version 450
+
+layout(location = 0) in vec2 tex_coord;
+layout(location = 1) in vec4 offset[3];
+
+layout(set = 0, binding = 0) uniform sampler2D color_tex;
+
+layout(location = 0) out vec2 edges;
+
+layout(push_constant, std430) uniform Params {
+	vec2 inv_size;
+	float threshold;
+	float reserved;
+}
+params;
+
+#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0
+
+void main() {
+	vec2 threshold = vec2(params.threshold);
+
+	vec4 delta;
+	vec3 C = texture(color_tex, tex_coord).rgb;
+
+	vec3 Cleft = texture(color_tex, offset[0].xy).rgb;
+	vec3 t = abs(C - Cleft);
+	delta.x = max(max(t.r, t.g), t.b);
+
+	vec3 Ctop = texture(color_tex, offset[0].zw).rgb;
+	t = abs(C - Ctop);
+	delta.y = max(max(t.r, t.g), t.b);
+
+	edges = step(threshold, delta.xy);
+
+	if (dot(edges, vec2(1.0, 1.0)) == 0.0) {
+		discard;
+	}
+
+	vec3 Cright = texture(color_tex, offset[1].xy).rgb;
+	t = abs(C - Cright);
+	delta.z = max(max(t.r, t.g), t.b);
+
+	vec3 Cbottom = texture(color_tex, offset[1].zw).rgb;
+	t = abs(C - Cbottom);
+	delta.w = max(max(t.r, t.g), t.b);
+
+	vec2 max_delta = max(delta.xy, delta.zw);
+
+	vec3 Cleftleft = texture(color_tex, offset[2].xy).rgb;
+	t = abs(Cleft - Cleftleft);
+	delta.z = max(max(t.r, t.g), t.b);
+
+	vec3 Ctoptop = texture(color_tex, offset[2].zw).rgb;
+	t = abs(Ctop - Ctoptop);
+	delta.w = max(max(t.r, t.g), t.b);
+
+	max_delta = max(max_delta.xy, delta.zw);
+	float final_delta = max(max_delta.x, max_delta.y);
+
+	edges.xy *= step(final_delta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy);
+}

+ 376 - 0
servers/rendering/renderer_rd/shaders/effects/smaa_weight_calculation.glsl

@@ -0,0 +1,376 @@
+/**
+ * Copyright (C) 2013 Jorge Jimenez ([email protected])
+ * Copyright (C) 2013 Jose I. Echevarria ([email protected])
+ * Copyright (C) 2013 Belen Masia ([email protected])
+ * Copyright (C) 2013 Fernando Navarro ([email protected])
+ * Copyright (C) 2013 Diego Gutierrez ([email protected])
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software. As clarification, there
+ * is no requirement that the copyright notice and permission be included in
+ * binary distributions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#[vertex]
+#version 450
+
+layout(location = 0) out vec2 tex_coord;
+layout(location = 1) out vec2 pix_coord;
+layout(location = 2) out vec4 offset[3];
+
+layout(push_constant, std430) uniform Params {
+	vec2 inv_size;
+	ivec2 size;
+
+	vec4 subsample_indices;
+}
+params;
+
+#define SMAA_MAX_SEARCH_STEPS 32
+
+void main() {
+	vec2 vertex_base;
+	if (gl_VertexIndex == 0) {
+		vertex_base = vec2(-1.0, -1.0);
+	} else if (gl_VertexIndex == 1) {
+		vertex_base = vec2(-1.0, 3.0);
+	} else {
+		vertex_base = vec2(3.0, -1.0);
+	}
+	gl_Position = vec4(vertex_base, 0.0, 1.0);
+	tex_coord = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
+	pix_coord = tex_coord * params.size.xy;
+
+	offset[0] = fma(params.inv_size.xyxy, vec4(-0.25, -0.125, 1.25, -0.125), tex_coord.xyxy);
+	offset[1] = fma(params.inv_size.xyxy, vec4(-0.125, -0.25, -0.125, 1.25), tex_coord.xyxy);
+	offset[2] = fma(params.inv_size.xxyy,
+			vec4(-2.0, 2.0, -2.0, 2.0) * SMAA_MAX_SEARCH_STEPS,
+			vec4(offset[0].xz, offset[1].yw));
+}
+
+#[fragment]
+#version 450
+
+layout(location = 0) in vec2 tex_coord;
+layout(location = 1) in vec2 pix_coord;
+layout(location = 2) in vec4 offset[3];
+layout(set = 0, binding = 0) uniform sampler2D edges_tex;
+layout(set = 1, binding = 0) uniform sampler2D area_tex;
+layout(set = 1, binding = 1) uniform sampler2D search_tex;
+layout(location = 0) out vec4 weights;
+
+layout(push_constant, std430) uniform Params {
+	vec2 inv_size;
+	ivec2 size;
+
+	vec4 subsample_indices;
+}
+params;
+
+#define SMAA_MAX_SEARCH_STEPS 32
+#define SMAA_MAX_SEARCH_STEPS_DIAG 16
+#define SMAA_CORNER_ROUNDING 25
+
+#ifndef SMAA_AREATEX_SELECT
+#define SMAA_AREATEX_SELECT(sample) sample.rg
+#endif
+
+#ifndef SMAA_SEARCHTEX_SELECT
+#define SMAA_SEARCHTEX_SELECT(sample) sample.r
+#endif
+
+#define SMAA_AREATEX_MAX_DISTANCE 16
+#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20
+#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0))
+#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0)
+#define SMAA_SEARCHTEX_SIZE vec2(66.0, 33.0)
+#define SMAA_SEARCHTEX_PACKED_SIZE vec2(64.0, 16.0)
+#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0)
+
+void SMAAMovc(bvec2 cond, inout vec2 variable, vec2 value) {
+	if (cond.x) {
+		variable.x = value.x;
+	}
+	if (cond.y) {
+		variable.y = value.y;
+	}
+}
+
+vec2 SMAADecodeDiagBilinearAccess(vec2 e) {
+	e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75);
+	return round(e);
+}
+
+vec4 SMAADecodeDiagBilinearAccess(vec4 e) {
+	e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75);
+	return round(e);
+}
+
+vec2 SMAASearchDiag1(vec2 tex_coord, vec2 dir, out vec2 e) {
+	vec4 coord = vec4(tex_coord, -1.0, 1.0);
+	vec3 t = vec3(params.inv_size.xy, 1.0);
+	while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) &&
+			coord.w > 0.9) {
+		coord.xyz = fma(t, vec3(dir, 1.0), coord.xyz);
+		e = textureLod(edges_tex, coord.xy, 0.0).rg;
+		coord.w = dot(e, vec2(0.5, 0.5));
+	}
+	return coord.zw;
+}
+
+vec2 SMAASearchDiag2(vec2 tex_coord, vec2 dir, out vec2 e) {
+	vec4 coord = vec4(tex_coord, -1.0, 1.0);
+	coord.x += 0.25 * params.inv_size.x;
+	vec3 t = vec3(params.inv_size.xy, 1.0);
+	while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) &&
+			coord.w > 0.9) {
+		coord.xyz = fma(t, vec3(dir, 1.0), coord.xyz);
+
+		e = textureLod(edges_tex, coord.xy, 0.0).rg;
+		e = SMAADecodeDiagBilinearAccess(e);
+
+		coord.w = dot(e, vec2(0.5, 0.5));
+	}
+	return coord.zw;
+}
+
+vec2 SMAAAreaDiag(vec2 dist, vec2 e, float offset) {
+	vec2 coord = fma(vec2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist);
+
+	coord = fma(SMAA_AREATEX_PIXEL_SIZE, coord, 0.5 * SMAA_AREATEX_PIXEL_SIZE);
+
+	coord.x += 0.5;
+
+	coord.y += SMAA_AREATEX_SUBTEX_SIZE * offset;
+
+	return SMAA_AREATEX_SELECT(textureLod(area_tex, coord, 0.0));
+}
+
+vec2 SMAACalculateDiagWeights(vec2 tex_coord, vec2 e, vec4 subsample_indices) {
+	vec2 weights = vec2(0.0, 0.0);
+
+	vec4 d;
+	vec2 end;
+	if (e.r > 0.0) {
+		d.xz = SMAASearchDiag1(tex_coord, vec2(-1.0, 1.0), end);
+		d.x += float(end.y > 0.9);
+	} else {
+		d.xz = vec2(0.0, 0.0);
+	}
+	d.yw = SMAASearchDiag1(tex_coord, vec2(1.0, -1.0), end);
+
+	if (d.x + d.y > 2.0) {
+		vec4 coords = fma(vec4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), params.inv_size.xyxy, tex_coord.xyxy);
+		vec4 c;
+		c.xy = textureLodOffset(edges_tex, coords.xy, 0.0, ivec2(-1, 0)).rg;
+		c.zw = textureLodOffset(edges_tex, coords.zw, 0.0, ivec2(1, 0)).rg;
+		c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw);
+
+		vec2 cc = fma(vec2(2.0, 2.0), c.xz, c.yw);
+
+		SMAAMovc(bvec2(step(0.9, d.zw)), cc, vec2(0.0, 0.0));
+
+		weights += SMAAAreaDiag(d.xy, cc, subsample_indices.z);
+	}
+
+	d.xz = SMAASearchDiag2(tex_coord, vec2(-1.0, -1.0), end);
+	if (textureLodOffset(edges_tex, tex_coord, 0.0, ivec2(1, 0)).r > 0.0) {
+		d.yw = SMAASearchDiag2(tex_coord, vec2(1.0, 1.0), end);
+		d.y += float(end.y > 0.9);
+	} else {
+		d.yw = vec2(0.0, 0.0);
+	}
+
+	if (d.x + d.y > 2.0) {
+		vec4 coords = fma(vec4(-d.x, -d.x, d.y, d.y), params.inv_size.xyxy, tex_coord.xyxy);
+		vec4 c;
+		c.x = textureLodOffset(edges_tex, coords.xy, 0.0, ivec2(-1, 0)).g;
+		c.y = textureLodOffset(edges_tex, coords.xy, 0.0, ivec2(0, -1)).r;
+		c.zw = textureLodOffset(edges_tex, coords.zw, 0.0, ivec2(1, 0)).gr;
+		vec2 cc = fma(vec2(2.0, 2.0), c.xz, c.yw);
+
+		SMAAMovc(bvec2(step(0.9, d.zw)), cc, vec2(0.0, 0.0));
+
+		weights += SMAAAreaDiag(d.xy, cc, subsample_indices.w).gr;
+	}
+
+	return weights;
+}
+
+float SMAASearchLength(vec2 e, float offset) {
+	vec2 scale = SMAA_SEARCHTEX_SIZE * vec2(0.5, -1.0);
+	vec2 bias = SMAA_SEARCHTEX_SIZE * vec2(offset, 1.0);
+
+	scale += vec2(-1.0, 1.0);
+	bias += vec2(0.5, -0.5);
+
+	scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE;
+	bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE;
+
+	return SMAA_SEARCHTEX_SELECT(textureLod(search_tex, fma(scale, e, bias), 0.0));
+}
+
+float SMAASearchXLeft(vec2 tex_coord, float end) {
+	vec2 e = vec2(0.0, 1.0);
+	while (tex_coord.x > end &&
+			e.g > 0.8281 &&
+			e.r == 0.0) {
+		e = textureLod(edges_tex, tex_coord, 0.0).rg;
+		tex_coord = fma(-vec2(2.0, 0.0), params.inv_size.xy, tex_coord);
+	}
+
+	float offset = fma(-(255.0 / 127.0), SMAASearchLength(e, 0.0), 3.25);
+	return fma(params.inv_size.x, offset, tex_coord.x);
+}
+
+float SMAASearchXRight(vec2 tex_coord, float end) {
+	vec2 e = vec2(0.0, 1.0);
+	while (tex_coord.x < end &&
+			e.g > 0.8281 &&
+			e.r == 0.0) {
+		e = textureLod(edges_tex, tex_coord, 0.0).rg;
+		tex_coord = fma(vec2(2.0, 0.0), params.inv_size.xy, tex_coord);
+	}
+
+	float offset = fma(-(255.0 / 127.0), SMAASearchLength(e, 0.5), 3.25);
+	return fma(-params.inv_size.x, offset, tex_coord.x);
+}
+
+float SMAASearchYUp(vec2 tex_coord, float end) {
+	vec2 e = vec2(1.0, 0.0);
+	while (tex_coord.y > end &&
+			e.r > 0.8281 &&
+			e.g == 0.0) {
+		e = textureLod(edges_tex, tex_coord, 0.0).rg;
+		tex_coord = fma(-vec2(0.0, 2.0), params.inv_size.xy, tex_coord);
+	}
+
+	float offset = fma(-(255.0 / 127.0), SMAASearchLength(e.gr, 0.0), 3.25);
+	return fma(params.inv_size.y, offset, tex_coord.y);
+}
+
+float SMAASearchYDown(vec2 tex_coord, float end) {
+	vec2 e = vec2(1.0, 0.0);
+	while (tex_coord.y < end &&
+			e.r > 0.8281 &&
+			e.g == 0.0) {
+		e = textureLod(edges_tex, tex_coord, 0.0).rg;
+		tex_coord = fma(vec2(0.0, 2.0), params.inv_size.xy, tex_coord);
+	}
+
+	float offset = fma(-(255.0 / 127.0), SMAASearchLength(e.gr, 0.5), 3.25);
+	return fma(-params.inv_size.y, offset, tex_coord.y);
+}
+
+vec2 SMAAArea(vec2 dist, float e1, float e2, float offset) {
+	vec2 tex_coord = fma(vec2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * vec2(e1, e2)), dist);
+
+	tex_coord = fma(SMAA_AREATEX_PIXEL_SIZE, tex_coord, 0.5 * SMAA_AREATEX_PIXEL_SIZE);
+
+	tex_coord.y = fma(SMAA_AREATEX_SUBTEX_SIZE, offset, tex_coord.y);
+
+	return SMAA_AREATEX_SELECT(textureLod(area_tex, tex_coord, 0.0));
+}
+
+void SMAADetectHorizontalCornerPattern(inout vec2 weights, vec4 coord, vec2 d) {
+	vec2 left_right = step(d.xy, d.yx);
+	vec2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * left_right;
+
+	rounding /= left_right.x + left_right.y;
+
+	vec2 factor = vec2(1.0, 1.0);
+	factor.x -= rounding.x * textureLodOffset(edges_tex, coord.xy, 0.0, ivec2(0, 1)).r;
+	factor.x -= rounding.y * textureLodOffset(edges_tex, coord.zw, 0.0, ivec2(1, 1)).r;
+	factor.y -= rounding.x * textureLodOffset(edges_tex, coord.xy, 0.0, ivec2(0, -2)).r;
+	factor.y -= rounding.y * textureLodOffset(edges_tex, coord.zw, 0.0, ivec2(1, -2)).r;
+
+	weights *= clamp(factor, 0.0, 1.0);
+}
+
+void SMAADetectVerticalCornerPattern(inout vec2 weights, vec4 coord, vec2 d) {
+	vec2 left_right = step(d.xy, d.yx);
+	vec2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * left_right;
+
+	rounding /= left_right.x + left_right.y;
+
+	vec2 factor = vec2(1.0, 1.0);
+	factor.x -= rounding.x * textureLodOffset(edges_tex, coord.xy, 0.0, ivec2(1, 0)).g;
+	factor.x -= rounding.y * textureLodOffset(edges_tex, coord.zw, 0.0, ivec2(1, 1)).g;
+	factor.y -= rounding.x * textureLodOffset(edges_tex, coord.xy, 0.0, ivec2(-2, 0)).g;
+	factor.y -= rounding.y * textureLodOffset(edges_tex, coord.zw, 0.0, ivec2(-2, 1)).g;
+
+	weights *= clamp(factor, 0.0, 1.0);
+}
+
+void main() {
+	weights = vec4(0.0, 0.0, 0.0, 0.0);
+	vec2 e = textureLod(edges_tex, tex_coord, 0.0).rg;
+
+	if (e.g > 0.0) { // Edge at north.
+		weights.rg = SMAACalculateDiagWeights(tex_coord, e, params.subsample_indices);
+		if (weights.r == -weights.g) {
+			vec2 d;
+			vec3 coords;
+			coords.x = SMAASearchXLeft(offset[0].xy, offset[2].x);
+			coords.y = offset[1].y;
+			d.x = coords.x;
+
+			float e1 = textureLod(edges_tex, coords.xy, 0.0).r;
+
+			coords.z = SMAASearchXRight(offset[0].zw, offset[2].y);
+			d.y = coords.z;
+
+			d = abs(round(fma(params.size.xx, d, -pix_coord.xx)));
+
+			vec2 sqrt_d = sqrt(d);
+
+			float e2 = textureLodOffset(edges_tex, coords.zy, 0.0, ivec2(1, 0)).r;
+
+			weights.rg = SMAAArea(sqrt_d, e1, e2, params.subsample_indices.y);
+
+			coords.y = tex_coord.y;
+			SMAADetectHorizontalCornerPattern(weights.rg, coords.xyzy, d);
+		} else {
+			e.r = 0.0;
+		}
+	}
+
+	if (e.r > 0.0) { // Edge at west.
+		vec2 d;
+		vec3 coords;
+		coords.y = SMAASearchYUp(offset[1].xy, offset[2].z);
+		coords.x = offset[0].x;
+		d.x = coords.y;
+
+		float e1 = textureLod(edges_tex, coords.xy, 0.0).g;
+
+		coords.z = SMAASearchYDown(offset[1].zw, offset[2].w);
+		d.y = coords.z;
+
+		d = abs(round(fma(params.size.yy, d, -pix_coord.yy)));
+
+		vec2 sqrt_d = sqrt(d);
+
+		float e2 = textureLodOffset(edges_tex, coords.xz, 0.0, ivec2(0, 1)).g;
+
+		weights.ba = SMAAArea(sqrt_d, e1, e2, params.subsample_indices.x);
+
+		coords.x = tex_coord.x;
+		SMAADetectVerticalCornerPattern(weights.ba, coords.xyxz, d);
+	}
+}

+ 1 - 0
servers/rendering/renderer_viewport.cpp

@@ -1363,6 +1363,7 @@ bool RendererViewport::viewport_is_using_hdr_2d(RID p_viewport) const {
 void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) {
 void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) {
 	Viewport *viewport = viewport_owner.get_or_null(p_viewport);
 	Viewport *viewport = viewport_owner.get_or_null(p_viewport);
 	ERR_FAIL_NULL(viewport);
 	ERR_FAIL_NULL(viewport);
+	ERR_FAIL_COND_EDMSG(p_mode != RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility", "Screen space AA is currently unavailable on the Compatibility renderer.");
 
 
 	if (viewport->screen_space_aa == p_mode) {
 	if (viewport->screen_space_aa == p_mode) {
 		return;
 		return;

+ 1 - 1
servers/rendering/storage/render_scene_buffers.cpp

@@ -57,7 +57,7 @@ void RenderSceneBuffersConfiguration::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &RenderSceneBuffersConfiguration::get_screen_space_aa);
 	ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &RenderSceneBuffersConfiguration::get_screen_space_aa);
 	ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &RenderSceneBuffersConfiguration::set_screen_space_aa);
 	ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &RenderSceneBuffersConfiguration::set_screen_space_aa);
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA"), "set_screen_space_aa", "get_screen_space_aa");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA,SMAA"), "set_screen_space_aa", "get_screen_space_aa");
 
 
 	ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &RenderSceneBuffersConfiguration::get_fsr_sharpness);
 	ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &RenderSceneBuffersConfiguration::get_fsr_sharpness);
 	ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &RenderSceneBuffersConfiguration::set_fsr_sharpness);
 	ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &RenderSceneBuffersConfiguration::set_fsr_sharpness);

+ 3 - 0
servers/rendering_server.cpp

@@ -2914,6 +2914,7 @@ void RenderingServer::_bind_methods() {
 
 
 	BIND_ENUM_CONSTANT(VIEWPORT_SCREEN_SPACE_AA_DISABLED);
 	BIND_ENUM_CONSTANT(VIEWPORT_SCREEN_SPACE_AA_DISABLED);
 	BIND_ENUM_CONSTANT(VIEWPORT_SCREEN_SPACE_AA_FXAA);
 	BIND_ENUM_CONSTANT(VIEWPORT_SCREEN_SPACE_AA_FXAA);
+	BIND_ENUM_CONSTANT(VIEWPORT_SCREEN_SPACE_AA_SMAA);
 	BIND_ENUM_CONSTANT(VIEWPORT_SCREEN_SPACE_AA_MAX);
 	BIND_ENUM_CONSTANT(VIEWPORT_SCREEN_SPACE_AA_MAX);
 
 
 	BIND_ENUM_CONSTANT(VIEWPORT_OCCLUSION_BUILD_QUALITY_LOW);
 	BIND_ENUM_CONSTANT(VIEWPORT_OCCLUSION_BUILD_QUALITY_LOW);
@@ -3676,6 +3677,8 @@ void RenderingServer::init() {
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/amount", PROPERTY_HINT_RANGE, "0.01,4.0,0.01"), 0.25);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/amount", PROPERTY_HINT_RANGE, "0.01,4.0,0.01"), 0.25);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/limit", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"), 0.18);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/limit", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"), 0.18);
 
 
+	GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/quality/smaa_edge_detection_threshold", PROPERTY_HINT_RANGE, "0.01,0.2,0.01"), 0.05);
+
 	{
 	{
 		String mode_hints;
 		String mode_hints;
 		String mode_hints_metal;
 		String mode_hints_metal;

+ 1 - 0
servers/rendering_server.h

@@ -1073,6 +1073,7 @@ public:
 	enum ViewportScreenSpaceAA {
 	enum ViewportScreenSpaceAA {
 		VIEWPORT_SCREEN_SPACE_AA_DISABLED,
 		VIEWPORT_SCREEN_SPACE_AA_DISABLED,
 		VIEWPORT_SCREEN_SPACE_AA_FXAA,
 		VIEWPORT_SCREEN_SPACE_AA_FXAA,
+		VIEWPORT_SCREEN_SPACE_AA_SMAA,
 		VIEWPORT_SCREEN_SPACE_AA_MAX,
 		VIEWPORT_SCREEN_SPACE_AA_MAX,
 	};
 	};
 
 

BIN
thirdparty/smaa/AreaTex.png


+ 24 - 0
thirdparty/smaa/LICENSE.txt

@@ -0,0 +1,24 @@
+Copyright (C) 2013 Jorge Jimenez ([email protected])
+Copyright (C) 2013 Jose I. Echevarria ([email protected])
+Copyright (C) 2013 Belen Masia ([email protected])
+Copyright (C) 2013 Fernando Navarro ([email protected])
+Copyright (C) 2013 Diego Gutierrez ([email protected])
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software. As clarification, there is no
+requirement that the copyright notice and permission be included in binary
+distributions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

BIN
thirdparty/smaa/SearchTex.png