Browse Source

Merge pull request #43167 from reduz/canvas-group

Implement CanvasGroup and CanvasItem clipping
Juan Linietsky 4 years ago
parent
commit
50da616eeb

+ 4 - 5
drivers/vulkan/rendering_device_vulkan.cpp

@@ -2947,7 +2947,7 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color,
 		image_memory_barrier.srcAccessMask = valid_texture_access;
 		image_memory_barrier.srcAccessMask = valid_texture_access;
 		image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
 		image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
 		image_memory_barrier.oldLayout = src_tex->layout;
 		image_memory_barrier.oldLayout = src_tex->layout;
-		image_memory_barrier.oldLayout = clear_layout;
+		image_memory_barrier.newLayout = clear_layout;
 
 
 		image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 		image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 		image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 		image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@@ -6795,13 +6795,12 @@ void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) {
 
 
 void RenderingDeviceVulkan::compute_list_end() {
 void RenderingDeviceVulkan::compute_list_end() {
 	ERR_FAIL_COND(!compute_list);
 	ERR_FAIL_COND(!compute_list);
-	const VkPipelineStageFlags dest_stage_mask = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
 	for (Set<Texture *>::Element *E = compute_list->state.textures_to_sampled_layout.front(); E; E = E->next()) {
 	for (Set<Texture *>::Element *E = compute_list->state.textures_to_sampled_layout.front(); E; E = E->next()) {
 		VkImageMemoryBarrier image_memory_barrier;
 		VkImageMemoryBarrier image_memory_barrier;
 		image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
 		image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
 		image_memory_barrier.pNext = nullptr;
 		image_memory_barrier.pNext = nullptr;
 		image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
 		image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
-		image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+		image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT;
 		image_memory_barrier.oldLayout = E->get()->layout;
 		image_memory_barrier.oldLayout = E->get()->layout;
 		image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 		image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 
 
@@ -6815,7 +6814,7 @@ void RenderingDeviceVulkan::compute_list_end() {
 		image_memory_barrier.subresourceRange.layerCount = E->get()->layers;
 		image_memory_barrier.subresourceRange.layerCount = E->get()->layers;
 
 
 		// TODO: Look at the usages in the compute list and determine tighter dst stage and access masks based on some "final" usage equivalent
 		// TODO: Look at the usages in the compute list and determine tighter dst stage and access masks based on some "final" usage equivalent
-		vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dest_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
+		vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
 
 
 		E->get()->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 		E->get()->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 	}
 	}
@@ -6825,7 +6824,7 @@ void RenderingDeviceVulkan::compute_list_end() {
 #ifdef FORCE_FULL_BARRIER
 #ifdef FORCE_FULL_BARRIER
 	_full_barrier(true);
 	_full_barrier(true);
 #else
 #else
-	_memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dest_stage_mask, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT, true);
+	_memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT, true);
 #endif
 #endif
 }
 }
 
 

+ 87 - 0
scene/2d/canvas_group.cpp

@@ -0,0 +1,87 @@
+/*************************************************************************/
+/*  canvas_group.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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 "canvas_group.h"
+
+void CanvasGroup::set_fit_margin(float p_fit_margin) {
+	ERR_FAIL_COND(p_fit_margin < 0.0);
+
+	fit_margin = p_fit_margin;
+	RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, fit_margin, use_mipmaps);
+
+	update();
+}
+
+float CanvasGroup::get_fit_margin() const {
+	return fit_margin;
+}
+
+void CanvasGroup::set_clear_margin(float p_clear_margin) {
+	ERR_FAIL_COND(p_clear_margin < 0.0);
+
+	clear_margin = p_clear_margin;
+	RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, clear_margin, use_mipmaps);
+
+	update();
+}
+
+float CanvasGroup::get_clear_margin() const {
+	return clear_margin;
+}
+
+void CanvasGroup::set_use_mipmaps(bool p_use_mipmaps) {
+	use_mipmaps = p_use_mipmaps;
+	RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, fit_margin, use_mipmaps);
+}
+bool CanvasGroup::is_using_mipmaps() const {
+	return use_mipmaps;
+}
+
+void CanvasGroup::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_fit_margin", "fit_margin"), &CanvasGroup::set_fit_margin);
+	ClassDB::bind_method(D_METHOD("get_fit_margin"), &CanvasGroup::get_fit_margin);
+
+	ClassDB::bind_method(D_METHOD("set_clear_margin", "clear_margin"), &CanvasGroup::set_clear_margin);
+	ClassDB::bind_method(D_METHOD("get_clear_margin"), &CanvasGroup::get_clear_margin);
+
+	ClassDB::bind_method(D_METHOD("set_use_mipmaps", "use_mipmaps"), &CanvasGroup::set_use_mipmaps);
+	ClassDB::bind_method(D_METHOD("is_using_mipmaps"), &CanvasGroup::is_using_mipmaps);
+
+	ADD_GROUP("Tweaks", "");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fit_margin", PROPERTY_HINT_RANGE, "0,1024,1.0,or_greater"), "set_fit_margin", "get_fit_margin");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clear_margin", PROPERTY_HINT_RANGE, "0,1024,1.0,or_greater"), "set_clear_margin", "get_clear_margin");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mipmaps"), "set_use_mipmaps", "is_using_mipmaps");
+}
+
+CanvasGroup::CanvasGroup() {
+	set_fit_margin(10.0); //sets things
+}
+CanvasGroup::~CanvasGroup() {
+}

+ 59 - 0
scene/2d/canvas_group.h

@@ -0,0 +1,59 @@
+/*************************************************************************/
+/*  canvas_group.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef CANVASGROUP_H
+#define CANVASGROUP_H
+
+#include "scene/2d/node_2d.h"
+
+class CanvasGroup : public Node2D {
+	GDCLASS(CanvasGroup, Node2D)
+	float fit_margin = 10.0;
+	float clear_margin = 10.0;
+	bool use_mipmaps = false;
+
+protected:
+	static void _bind_methods();
+
+public:
+	void set_fit_margin(float p_fit_margin);
+	float get_fit_margin() const;
+
+	void set_clear_margin(float p_clear_margin);
+	float get_clear_margin() const;
+
+	void set_use_mipmaps(bool p_use_mipmaps);
+	bool is_using_mipmaps() const;
+
+	CanvasGroup();
+	~CanvasGroup();
+};
+
+#endif // CANVASGROUP_H

+ 22 - 0
scene/main/canvas_item.cpp

@@ -32,6 +32,7 @@
 
 
 #include "core/input/input.h"
 #include "core/input/input.h"
 #include "core/message_queue.h"
 #include "core/message_queue.h"
+#include "scene/2d/canvas_group.h"
 #include "scene/main/canvas_layer.h"
 #include "scene/main/canvas_layer.h"
 #include "scene/main/viewport.h"
 #include "scene/main/viewport.h"
 #include "scene/main/window.h"
 #include "scene/main/window.h"
@@ -1199,6 +1200,9 @@ void CanvasItem::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat);
 	ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat);
 	ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat);
 	ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat);
 
 
+	ClassDB::bind_method(D_METHOD("set_clip_children", "enable"), &CanvasItem::set_clip_children);
+	ClassDB::bind_method(D_METHOD("is_clipping_children"), &CanvasItem::is_clipping_children);
+
 	BIND_VMETHOD(MethodInfo("_draw"));
 	BIND_VMETHOD(MethodInfo("_draw"));
 
 
 	ADD_GROUP("Visibility", "");
 	ADD_GROUP("Visibility", "");
@@ -1208,6 +1212,7 @@ void CanvasItem::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", 0), "_set_on_top", "_is_on_top"); //compatibility
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", 0), "_set_on_top", "_is_on_top"); //compatibility
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
 
 
 	ADD_GROUP("Texture", "texture_");
 	ADD_GROUP("Texture", "texture_");
@@ -1383,6 +1388,22 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) {
 	_change_notify();
 	_change_notify();
 }
 }
 
 
+void CanvasItem::set_clip_children(bool p_enabled) {
+	if (clip_children == p_enabled) {
+		return;
+	}
+	clip_children = p_enabled;
+
+	if (Object::cast_to<CanvasGroup>(this) != nullptr) {
+		//avoid accidental bugs, make this not work on CanvasGroup
+		return;
+	}
+	RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), clip_children ? RS::CANVAS_GROUP_MODE_OPAQUE : RS::CANVAS_GROUP_MODE_DISABLED);
+}
+bool CanvasItem::is_clipping_children() const {
+	return clip_children;
+}
+
 CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const {
 CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const {
 	return texture_repeat;
 	return texture_repeat;
 }
 }
@@ -1399,6 +1420,7 @@ CanvasItem::CanvasItem() :
 	first_draw = false;
 	first_draw = false;
 	drawing = false;
 	drawing = false;
 	behind = false;
 	behind = false;
+	clip_children = false;
 	block_transform_notify = false;
 	block_transform_notify = false;
 	canvas_layer = nullptr;
 	canvas_layer = nullptr;
 	use_parent_material = false;
 	use_parent_material = false;

+ 4 - 0
scene/main/canvas_item.h

@@ -200,6 +200,7 @@ private:
 	Window *window;
 	Window *window;
 	bool first_draw;
 	bool first_draw;
 	bool visible;
 	bool visible;
+	bool clip_children;
 	bool pending_update;
 	bool pending_update;
 	bool top_level;
 	bool top_level;
 	bool drawing;
 	bool drawing;
@@ -315,6 +316,9 @@ public:
 
 
 	void update();
 	void update();
 
 
+	void set_clip_children(bool p_enabled);
+	bool is_clipping_children() const;
+
 	virtual void set_light_mask(int p_light_mask);
 	virtual void set_light_mask(int p_light_mask);
 	int get_light_mask() const;
 	int get_light_mask() const;
 
 

+ 2 - 0
scene/register_scene_types.cpp

@@ -38,6 +38,7 @@
 #include "scene/2d/audio_stream_player_2d.h"
 #include "scene/2d/audio_stream_player_2d.h"
 #include "scene/2d/back_buffer_copy.h"
 #include "scene/2d/back_buffer_copy.h"
 #include "scene/2d/camera_2d.h"
 #include "scene/2d/camera_2d.h"
+#include "scene/2d/canvas_group.h"
 #include "scene/2d/canvas_modulate.h"
 #include "scene/2d/canvas_modulate.h"
 #include "scene/2d/collision_polygon_2d.h"
 #include "scene/2d/collision_polygon_2d.h"
 #include "scene/2d/collision_shape_2d.h"
 #include "scene/2d/collision_shape_2d.h"
@@ -602,6 +603,7 @@ void register_scene_types() {
 	/* REGISTER 2D */
 	/* REGISTER 2D */
 
 
 	ClassDB::register_class<Node2D>();
 	ClassDB::register_class<Node2D>();
+	ClassDB::register_class<CanvasGroup>();
 	ClassDB::register_class<CPUParticles2D>();
 	ClassDB::register_class<CPUParticles2D>();
 	ClassDB::register_class<GPUParticles2D>();
 	ClassDB::register_class<GPUParticles2D>();
 	ClassDB::register_class<Sprite2D>();
 	ClassDB::register_class<Sprite2D>();

+ 15 - 2
scene/resources/dynamic_font.cpp

@@ -458,8 +458,21 @@ DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyp
 			//zero texture
 			//zero texture
 			uint8_t *w = tex.imgdata.ptrw();
 			uint8_t *w = tex.imgdata.ptrw();
 			ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
 			ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
-			for (int i = 0; i < texsize * texsize * p_color_size; i++) {
-				w[i] = 0;
+
+			// Initialize the texture to all-white pixels to prevent artifacts when the
+			// font is displayed at a non-default scale with filtering enabled.
+			if (p_color_size == 2) {
+				for (int i = 0; i < texsize * texsize * p_color_size; i += 2) {
+					w[i + 0] = 255;
+					w[i + 1] = 0;
+				}
+			} else {
+				for (int i = 0; i < texsize * texsize * p_color_size; i += 4) {
+					w[i + 0] = 255;
+					w[i + 1] = 255;
+					w[i + 2] = 255;
+					w[i + 3] = 0;
+				}
 			}
 			}
 		}
 		}
 		tex.offsets.resize(texsize);
 		tex.offsets.resize(texsize);

+ 16 - 2
servers/rendering/rasterizer.h

@@ -815,7 +815,8 @@ public:
 		CANVAS_RECT_FLIP_H = 4,
 		CANVAS_RECT_FLIP_H = 4,
 		CANVAS_RECT_FLIP_V = 8,
 		CANVAS_RECT_FLIP_V = 8,
 		CANVAS_RECT_TRANSPOSE = 16,
 		CANVAS_RECT_TRANSPOSE = 16,
-		CANVAS_RECT_CLIP_UV = 32
+		CANVAS_RECT_CLIP_UV = 32,
+		CANVAS_RECT_IS_GROUP = 64,
 	};
 	};
 
 
 	struct Light {
 	struct Light {
@@ -1060,7 +1061,16 @@ public:
 		bool visible;
 		bool visible;
 		bool behind;
 		bool behind;
 		bool update_when_visible;
 		bool update_when_visible;
-		//RS::MaterialBlendMode blend_mode;
+
+		struct CanvasGroup {
+			RS::CanvasGroupMode mode;
+			bool fit_empty;
+			float fit_margin;
+			bool blur_mipmaps;
+			float clear_margin;
+		};
+
+		CanvasGroup *canvas_group = nullptr;
 		int light_mask;
 		int light_mask;
 		int z_final;
 		int z_final;
 
 
@@ -1084,6 +1094,7 @@ public:
 		Rect2 final_clip_rect;
 		Rect2 final_clip_rect;
 		Item *final_clip_owner;
 		Item *final_clip_owner;
 		Item *material_owner;
 		Item *material_owner;
+		Item *canvas_group_owner;
 		ViewportRender *vp_render;
 		ViewportRender *vp_render;
 		bool distance_field;
 		bool distance_field;
 		bool light_masked;
 		bool light_masked;
@@ -1242,6 +1253,8 @@ public:
 		}
 		}
 
 
 		void clear() {
 		void clear() {
+			// The first one is always allocated on heap
+			// the rest go in the blocks
 			Command *c = commands;
 			Command *c = commands;
 			while (c) {
 			while (c) {
 				Command *n = c->next;
 				Command *n = c->next;
@@ -1282,6 +1295,7 @@ public:
 			vp_render = nullptr;
 			vp_render = nullptr;
 			next = nullptr;
 			next = nullptr;
 			final_clip_owner = nullptr;
 			final_clip_owner = nullptr;
+			canvas_group_owner = nullptr;
 			clip = false;
 			clip = false;
 			final_modulate = Color(1, 1, 1, 1);
 			final_modulate = Color(1, 1, 1, 1);
 			visible = true;
 			visible = true;

+ 93 - 20
servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp

@@ -1102,28 +1102,36 @@ RID RasterizerCanvasRD::_create_base_uniform_set(RID p_to_render_target, bool p_
 	return uniform_set;
 	return uniform_set;
 }
 }
 
 
-void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights) {
+void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer) {
 	Item *current_clip = nullptr;
 	Item *current_clip = nullptr;
 
 
 	Transform2D canvas_transform_inverse = p_canvas_transform_inverse;
 	Transform2D canvas_transform_inverse = p_canvas_transform_inverse;
 
 
-	RID framebuffer = storage->render_target_get_rd_framebuffer(p_to_render_target);
-
-	Vector<Color> clear_colors;
+	RID framebuffer;
+	RID fb_uniform_set;
 	bool clear = false;
 	bool clear = false;
-	if (storage->render_target_is_clear_requested(p_to_render_target)) {
-		clear = true;
-		clear_colors.push_back(storage->render_target_get_clear_request_color(p_to_render_target));
-		storage->render_target_disable_clear_request(p_to_render_target);
-	}
+	Vector<Color> clear_colors;
+
+	if (p_to_backbuffer) {
+		framebuffer = storage->render_target_get_rd_backbuffer_framebuffer(p_to_render_target);
+		fb_uniform_set = storage->render_target_get_backbuffer_uniform_set(p_to_render_target);
+	} else {
+		framebuffer = storage->render_target_get_rd_framebuffer(p_to_render_target);
+
+		if (storage->render_target_is_clear_requested(p_to_render_target)) {
+			clear = true;
+			clear_colors.push_back(storage->render_target_get_clear_request_color(p_to_render_target));
+			storage->render_target_disable_clear_request(p_to_render_target);
+		}
 #ifndef _MSC_VER
 #ifndef _MSC_VER
 #warning TODO obtain from framebuffer format eventually when this is implemented
 #warning TODO obtain from framebuffer format eventually when this is implemented
 #endif
 #endif
 
 
-	RID fb_uniform_set = storage->render_target_get_framebuffer_uniform_set(p_to_render_target);
+		fb_uniform_set = storage->render_target_get_framebuffer_uniform_set(p_to_render_target);
+	}
 
 
 	if (fb_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fb_uniform_set)) {
 	if (fb_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fb_uniform_set)) {
-		fb_uniform_set = _create_base_uniform_set(p_to_render_target, false);
+		fb_uniform_set = _create_base_uniform_set(p_to_render_target, p_to_backbuffer);
 	}
 	}
 
 
 	RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
 	RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
@@ -1152,10 +1160,16 @@ void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count,
 			}
 			}
 		}
 		}
 
 
-		if (ci->material != prev_material) {
+		RID material = ci->material;
+
+		if (material.is_null() && ci->canvas_group != nullptr) {
+			material = default_canvas_group_material;
+		}
+
+		if (material != prev_material) {
 			MaterialData *material_data = nullptr;
 			MaterialData *material_data = nullptr;
-			if (ci->material.is_valid()) {
-				material_data = (MaterialData *)storage->material_get_data(ci->material, RasterizerStorageRD::SHADER_TYPE_2D);
+			if (material.is_valid()) {
+				material_data = (MaterialData *)storage->material_get_data(material, RasterizerStorageRD::SHADER_TYPE_2D);
 			}
 			}
 
 
 			if (material_data) {
 			if (material_data) {
@@ -1174,7 +1188,7 @@ void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count,
 
 
 		_render_item(draw_list, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants);
 		_render_item(draw_list, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants);
 
 
-		prev_material = ci->material;
+		prev_material = material;
 	}
 	}
 
 
 	RD::get_singleton()->draw_list_end();
 	RD::get_singleton()->draw_list_end();
@@ -1306,8 +1320,10 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
 	Rect2 back_buffer_rect;
 	Rect2 back_buffer_rect;
 	bool backbuffer_copy = false;
 	bool backbuffer_copy = false;
 
 
+	Item *canvas_group_owner = nullptr;
+
 	while (ci) {
 	while (ci) {
-		if (ci->copy_back_buffer) {
+		if (ci->copy_back_buffer && canvas_group_owner == nullptr) {
 			backbuffer_copy = true;
 			backbuffer_copy = true;
 
 
 			if (ci->copy_back_buffer->full) {
 			if (ci->copy_back_buffer->full) {
@@ -1320,7 +1336,7 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
 		if (ci->material.is_valid()) {
 		if (ci->material.is_valid()) {
 			MaterialData *md = (MaterialData *)storage->material_get_data(ci->material, RasterizerStorageRD::SHADER_TYPE_2D);
 			MaterialData *md = (MaterialData *)storage->material_get_data(ci->material, RasterizerStorageRD::SHADER_TYPE_2D);
 			if (md && md->shader_data->valid) {
 			if (md && md->shader_data->valid) {
-				if (md->shader_data->uses_screen_texture) {
+				if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) {
 					if (!material_screen_texture_found) {
 					if (!material_screen_texture_found) {
 						backbuffer_copy = true;
 						backbuffer_copy = true;
 						back_buffer_rect = Rect2();
 						back_buffer_rect = Rect2();
@@ -1338,12 +1354,44 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
 			}
 			}
 		}
 		}
 
 
+		if (ci->canvas_group_owner != nullptr) {
+			if (canvas_group_owner == nullptr) {
+				//Canvas group begins here, render until before this item
+				_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
+				item_count = 0;
+
+				Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
+
+				if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) {
+					storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
+				} else {
+					storage->render_target_clear_back_buffer(p_to_render_target, group_rect, Color(0, 0, 0, 0));
+				}
+
+				backbuffer_copy = false;
+				canvas_group_owner = ci->canvas_group_owner; //continue until owner found
+			}
+
+			ci->canvas_group_owner = nullptr; //must be cleared
+		}
+
+		if (ci == canvas_group_owner) {
+			_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, true);
+			item_count = 0;
+
+			if (ci->canvas_group->blur_mipmaps) {
+				storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, ci->global_rect_cache);
+			}
+
+			canvas_group_owner = nullptr;
+		}
+
 		if (backbuffer_copy) {
 		if (backbuffer_copy) {
 			//render anything pending, including clearing if no items
 			//render anything pending, including clearing if no items
 			_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
 			_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
 			item_count = 0;
 			item_count = 0;
 
 
-			storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect);
+			storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, true);
 
 
 			backbuffer_copy = false;
 			backbuffer_copy = false;
 			material_screen_texture_found = true; //after a backbuffer copy, screen texture makes no further copies
 			material_screen_texture_found = true; //after a backbuffer copy, screen texture makes no further copies
@@ -1672,10 +1720,11 @@ void RasterizerCanvasRD::ShaderData::set_code(const String &p_code) {
 		} break;
 		} break;
 		case BLEND_MODE_MIX: {
 		case BLEND_MODE_MIX: {
 			attachment.enable_blend = true;
 			attachment.enable_blend = true;
-			attachment.alpha_blend_op = RD::BLEND_OP_ADD;
 			attachment.color_blend_op = RD::BLEND_OP_ADD;
 			attachment.color_blend_op = RD::BLEND_OP_ADD;
 			attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
 			attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
 			attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
 			attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+
+			attachment.alpha_blend_op = RD::BLEND_OP_ADD;
 			attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
 			attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
 			attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
 			attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
 
 
@@ -2013,6 +2062,20 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
 		shader.default_version = shader.canvas_shader.version_create();
 		shader.default_version = shader.canvas_shader.version_create();
 		shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_QUAD);
 		shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_QUAD);
 
 
+		RD::PipelineColorBlendState blend_state;
+		RD::PipelineColorBlendState::Attachment blend_attachment;
+
+		blend_attachment.enable_blend = true;
+		blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
+		blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+		blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+
+		blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+		blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+		blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+
+		blend_state.attachments.push_back(blend_attachment);
+
 		for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) {
 		for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) {
 			for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) {
 			for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) {
 				RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = {
 				RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = {
@@ -2054,7 +2117,7 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
 				};
 				};
 
 
 				RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[i][j]);
 				RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[i][j]);
-				shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0);
+				shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -2260,6 +2323,13 @@ RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
 
 
 	state.time = 0;
 	state.time = 0;
 
 
+	{
+		default_canvas_group_shader = storage->shader_create();
+		storage->shader_set_code(default_canvas_group_shader, "shader_type canvas_item; \nvoid fragment() {\n\tvec4 c = textureLod(SCREEN_TEXTURE,SCREEN_UV,0.0); if (c.a > 0.0001) c.rgb/=c.a; COLOR *= c; \n}\n");
+		default_canvas_group_material = storage->material_create();
+		storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader);
+	}
+
 	static_assert(sizeof(PushConstant) == 128);
 	static_assert(sizeof(PushConstant) == 128);
 }
 }
 
 
@@ -2307,6 +2377,9 @@ void RasterizerCanvasRD::set_shadow_texture_size(int p_size) {
 RasterizerCanvasRD::~RasterizerCanvasRD() {
 RasterizerCanvasRD::~RasterizerCanvasRD() {
 	//canvas state
 	//canvas state
 
 
+	storage->free(default_canvas_group_material);
+	storage->free(default_canvas_group_shader);
+
 	{
 	{
 		if (state.canvas_state_buffer.is_valid()) {
 		if (state.canvas_state_buffer.is_valid()) {
 			RD::get_singleton()->free(state.canvas_state_buffer);
 			RD::get_singleton()->free(state.canvas_state_buffer);

+ 4 - 1
servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h

@@ -391,6 +391,9 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 
 
 	RID default_canvas_texture;
 	RID default_canvas_texture;
 
 
+	RID default_canvas_group_shader;
+	RID default_canvas_group_material;
+
 	RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
 	RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
 	RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
 	RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
 
 
@@ -398,7 +401,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 
 
 	inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead.
 	inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead.
 	void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants);
 	void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants);
-	void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights);
+	void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false);
 
 
 	_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
 	_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
 	_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
 	_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);

+ 34 - 3
servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp

@@ -246,7 +246,7 @@ void RasterizerEffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_fr
 	RD::get_singleton()->draw_list_end();
 	RD::get_singleton()->draw_list_end();
 }
 }
 
 
-void RasterizerEffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_all_source, bool p_8_bit_dst) {
+void RasterizerEffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_all_source, bool p_8_bit_dst, bool p_alpha_to_one) {
 	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
 	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
 	if (p_flip_y) {
 	if (p_flip_y) {
 		copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
 		copy.push_constant.flags |= COPY_FLAG_FLIP_Y;
@@ -260,6 +260,10 @@ void RasterizerEffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_textu
 		copy.push_constant.flags |= COPY_FLAG_ALL_SOURCE;
 		copy.push_constant.flags |= COPY_FLAG_ALL_SOURCE;
 	}
 	}
 
 
+	if (p_alpha_to_one) {
+		copy.push_constant.flags |= COPY_FLAG_ALPHA_TO_ONE;
+	}
+
 	copy.push_constant.section[0] = 0;
 	copy.push_constant.section[0] = 0;
 	copy.push_constant.section[1] = 0;
 	copy.push_constant.section[1] = 0;
 	copy.push_constant.section[2] = p_rect.size.width;
 	copy.push_constant.section[2] = p_rect.size.width;
@@ -354,6 +358,31 @@ void RasterizerEffectsRD::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest
 	RD::get_singleton()->compute_list_end();
 	RD::get_singleton()->compute_list_end();
 }
 }
 
 
+void RasterizerEffectsRD::set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst) {
+	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
+
+	copy.push_constant.section[0] = 0;
+	copy.push_constant.section[1] = 0;
+	copy.push_constant.section[2] = p_region.size.width;
+	copy.push_constant.section[3] = p_region.size.height;
+	copy.push_constant.target[0] = p_region.position.x;
+	copy.push_constant.target[1] = p_region.position.y;
+	copy.push_constant.set_color[0] = p_color.r;
+	copy.push_constant.set_color[1] = p_color.g;
+	copy.push_constant.set_color[2] = p_color.b;
+	copy.push_constant.set_color[3] = p_color.a;
+
+	int32_t x_groups = (p_region.size.width - 1) / 8 + 1;
+	int32_t y_groups = (p_region.size.height - 1) / 8 + 1;
+
+	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8bit_dst ? COPY_MODE_SET_COLOR_8BIT : COPY_MODE_SET_COLOR]);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 3);
+	RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
+	RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
+	RD::get_singleton()->compute_list_end();
+}
+
 void RasterizerEffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst) {
 void RasterizerEffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst) {
 	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
 	zeromem(&copy.push_constant, sizeof(CopyPushConstant));
 
 
@@ -369,7 +398,7 @@ void RasterizerEffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture,
 	RD::DrawListID compute_list = RD::get_singleton()->compute_list_begin();
 	RD::DrawListID compute_list = RD::get_singleton()->compute_list_begin();
 	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8bit_dst ? COPY_MODE_GAUSSIAN_COPY_8BIT : COPY_MODE_GAUSSIAN_COPY]);
 	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8bit_dst ? COPY_MODE_GAUSSIAN_COPY_8BIT : COPY_MODE_GAUSSIAN_COPY]);
 	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
 	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
-	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_back_texture), 0);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_back_texture), 3);
 
 
 	copy.push_constant.flags = base_flags | COPY_FLAG_HORIZONTAL;
 	copy.push_constant.flags = base_flags | COPY_FLAG_HORIZONTAL;
 	RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
 	RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
@@ -380,7 +409,7 @@ void RasterizerEffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture,
 
 
 	//VERTICAL
 	//VERTICAL
 	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_back_texture), 0);
 	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_back_texture), 0);
-	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_texture), 0);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_texture), 3);
 
 
 	copy.push_constant.flags = base_flags;
 	copy.push_constant.flags = base_flags;
 	RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
 	RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
@@ -1346,6 +1375,8 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n");
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n");
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n#define DST_IMAGE_8BIT\n");
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n#define DST_IMAGE_8BIT\n");
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n");
 		copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n");
+		copy_modes.push_back("\n#define MODE_SET_COLOR\n");
+		copy_modes.push_back("\n#define MODE_SET_COLOR\n#define DST_IMAGE_8BIT\n");
 		copy_modes.push_back("\n#define MODE_MIPMAP\n");
 		copy_modes.push_back("\n#define MODE_MIPMAP\n");
 		copy_modes.push_back("\n#define MODE_LINEARIZE_DEPTH_COPY\n");
 		copy_modes.push_back("\n#define MODE_LINEARIZE_DEPTH_COPY\n");
 		copy_modes.push_back("\n#define MODE_CUBEMAP_TO_PANORAMA\n");
 		copy_modes.push_back("\n#define MODE_CUBEMAP_TO_PANORAMA\n");

+ 8 - 2
servers/rendering/rasterizer_rd/rasterizer_effects_rd.h

@@ -66,6 +66,8 @@ class RasterizerEffectsRD {
 		COPY_MODE_SIMPLY_COPY,
 		COPY_MODE_SIMPLY_COPY,
 		COPY_MODE_SIMPLY_COPY_8BIT,
 		COPY_MODE_SIMPLY_COPY_8BIT,
 		COPY_MODE_SIMPLY_COPY_DEPTH,
 		COPY_MODE_SIMPLY_COPY_DEPTH,
+		COPY_MODE_SET_COLOR,
+		COPY_MODE_SET_COLOR_8BIT,
 		COPY_MODE_MIPMAP,
 		COPY_MODE_MIPMAP,
 		COPY_MODE_LINEARIZE_DEPTH,
 		COPY_MODE_LINEARIZE_DEPTH,
 		COPY_MODE_CUBE_TO_PANORAMA,
 		COPY_MODE_CUBE_TO_PANORAMA,
@@ -83,7 +85,8 @@ class RasterizerEffectsRD {
 		COPY_FLAG_FLIP_Y = (1 << 5),
 		COPY_FLAG_FLIP_Y = (1 << 5),
 		COPY_FLAG_FORCE_LUMINANCE = (1 << 6),
 		COPY_FLAG_FORCE_LUMINANCE = (1 << 6),
 		COPY_FLAG_ALL_SOURCE = (1 << 7),
 		COPY_FLAG_ALL_SOURCE = (1 << 7),
-		COPY_FLAG_HIGH_QUALITY_GLOW = (1 << 8)
+		COPY_FLAG_HIGH_QUALITY_GLOW = (1 << 8),
+		COPY_FLAG_ALPHA_TO_ONE = (1 << 9),
 	};
 	};
 
 
 	struct CopyPushConstant {
 	struct CopyPushConstant {
@@ -105,6 +108,8 @@ class RasterizerEffectsRD {
 		float camera_z_far;
 		float camera_z_far;
 		float camera_z_near;
 		float camera_z_near;
 		uint32_t pad2[2];
 		uint32_t pad2[2];
+		//SET color
+		float set_color[4];
 	};
 	};
 
 
 	struct Copy {
 	struct Copy {
@@ -603,12 +608,13 @@ class RasterizerEffectsRD {
 
 
 public:
 public:
 	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID());
 	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID());
-	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
+	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false);
 	void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
 	void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
 	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
 	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
 	void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
 	void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
 	void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
 	void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
 	void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
 	void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
+	void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
 	void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
 	void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
 
 
 	void cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
 	void cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);

+ 85 - 6
servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp

@@ -6131,12 +6131,18 @@ void RasterizerStorageRD::_create_render_target_backbuffer(RenderTarget *rt) {
 	tf.width = rt->size.width;
 	tf.width = rt->size.width;
 	tf.height = rt->size.height;
 	tf.height = rt->size.height;
 	tf.type = RD::TEXTURE_TYPE_2D;
 	tf.type = RD::TEXTURE_TYPE_2D;
-	tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+	tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
 	tf.mipmaps = mipmaps_required;
 	tf.mipmaps = mipmaps_required;
 
 
 	rt->backbuffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
 	rt->backbuffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
 	rt->backbuffer_mipmap0 = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, 0);
 	rt->backbuffer_mipmap0 = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, 0);
 
 
+	{
+		Vector<RID> fb_tex;
+		fb_tex.push_back(rt->backbuffer_mipmap0);
+		rt->backbuffer_fb = RD::get_singleton()->framebuffer_create(fb_tex);
+	}
+
 	if (rt->framebuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->framebuffer_uniform_set)) {
 	if (rt->framebuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->framebuffer_uniform_set)) {
 		//the new one will require the backbuffer.
 		//the new one will require the backbuffer.
 		RD::get_singleton()->free(rt->framebuffer_uniform_set);
 		RD::get_singleton()->free(rt->framebuffer_uniform_set);
@@ -6245,6 +6251,17 @@ RID RasterizerStorageRD::render_target_get_rd_backbuffer(RID p_render_target) {
 	return rt->backbuffer;
 	return rt->backbuffer;
 }
 }
 
 
+RID RasterizerStorageRD::render_target_get_rd_backbuffer_framebuffer(RID p_render_target) {
+	RenderTarget *rt = render_target_owner.getornull(p_render_target);
+	ERR_FAIL_COND_V(!rt, RID());
+
+	if (!rt->backbuffer.is_valid()) {
+		_create_render_target_backbuffer(rt);
+	}
+
+	return rt->backbuffer_fb;
+}
+
 void RasterizerStorageRD::render_target_request_clear(RID p_render_target, const Color &p_clear_color) {
 void RasterizerStorageRD::render_target_request_clear(RID p_render_target, const Color &p_clear_color) {
 	RenderTarget *rt = render_target_owner.getornull(p_render_target);
 	RenderTarget *rt = render_target_owner.getornull(p_render_target);
 	ERR_FAIL_COND(!rt);
 	ERR_FAIL_COND(!rt);
@@ -6283,21 +6300,30 @@ void RasterizerStorageRD::render_target_do_clear_request(RID p_render_target) {
 	rt->clear_requested = false;
 	rt->clear_requested = false;
 }
 }
 
 
-void RasterizerStorageRD::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region) {
+void RasterizerStorageRD::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) {
 	RenderTarget *rt = render_target_owner.getornull(p_render_target);
 	RenderTarget *rt = render_target_owner.getornull(p_render_target);
 	ERR_FAIL_COND(!rt);
 	ERR_FAIL_COND(!rt);
 	if (!rt->backbuffer.is_valid()) {
 	if (!rt->backbuffer.is_valid()) {
 		_create_render_target_backbuffer(rt);
 		_create_render_target_backbuffer(rt);
 	}
 	}
 
 
-	Rect2i region = p_region;
-	if (region == Rect2i()) {
+	Rect2i region;
+	if (p_region == Rect2i()) {
 		region.size = rt->size;
 		region.size = rt->size;
+	} else {
+		region = Rect2i(Size2i(), rt->size).clip(p_region);
+		if (region.size == Size2i()) {
+			return; //nothing to do
+		}
 	}
 	}
 
 
 	//single texture copy for backbuffer
 	//single texture copy for backbuffer
-	RD::get_singleton()->texture_copy(rt->color, rt->backbuffer_mipmap0, Vector3(region.position.x, region.position.y, 0), Vector3(region.position.x, region.position.y, 0), Vector3(region.size.x, region.size.y, 1), 0, 0, 0, 0, true);
-	//effects.copy(rt->color, rt->backbuffer_fb, blur_region);
+	//RD::get_singleton()->texture_copy(rt->color, rt->backbuffer_mipmap0, Vector3(region.position.x, region.position.y, 0), Vector3(region.position.x, region.position.y, 0), Vector3(region.size.x, region.size.y, 1), 0, 0, 0, 0, true);
+	effects.copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true);
+
+	if (!p_gen_mipmaps) {
+		return;
+	}
 
 
 	//then mipmap blur
 	//then mipmap blur
 	RID prev_texture = rt->color; //use color, not backbuffer, as bb has mipmaps.
 	RID prev_texture = rt->color; //use color, not backbuffer, as bb has mipmaps.
@@ -6314,6 +6340,59 @@ void RasterizerStorageRD::render_target_copy_to_back_buffer(RID p_render_target,
 	}
 	}
 }
 }
 
 
+void RasterizerStorageRD::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) {
+	RenderTarget *rt = render_target_owner.getornull(p_render_target);
+	ERR_FAIL_COND(!rt);
+	if (!rt->backbuffer.is_valid()) {
+		_create_render_target_backbuffer(rt);
+	}
+
+	Rect2i region;
+	if (p_region == Rect2i()) {
+		region.size = rt->size;
+	} else {
+		region = Rect2i(Size2i(), rt->size).clip(p_region);
+		if (region.size == Size2i()) {
+			return; //nothing to do
+		}
+	}
+
+	//single texture copy for backbuffer
+	effects.set_color(rt->backbuffer_mipmap0, p_color, region, true);
+}
+
+void RasterizerStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region) {
+	RenderTarget *rt = render_target_owner.getornull(p_render_target);
+	ERR_FAIL_COND(!rt);
+	if (!rt->backbuffer.is_valid()) {
+		_create_render_target_backbuffer(rt);
+	}
+
+	Rect2i region;
+	if (p_region == Rect2i()) {
+		region.size = rt->size;
+	} else {
+		region = Rect2i(Size2i(), rt->size).clip(p_region);
+		if (region.size == Size2i()) {
+			return; //nothing to do
+		}
+	}
+
+	//then mipmap blur
+	RID prev_texture = rt->backbuffer_mipmap0;
+
+	for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) {
+		region.position.x >>= 1;
+		region.position.y >>= 1;
+		region.size.x = MAX(1, region.size.x >> 1);
+		region.size.y = MAX(1, region.size.y >> 1);
+
+		const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i];
+		effects.gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
+		prev_texture = mm.mipmap;
+	}
+}
+
 RID RasterizerStorageRD::render_target_get_framebuffer_uniform_set(RID p_render_target) {
 RID RasterizerStorageRD::render_target_get_framebuffer_uniform_set(RID p_render_target) {
 	RenderTarget *rt = render_target_owner.getornull(p_render_target);
 	RenderTarget *rt = render_target_owner.getornull(p_render_target);
 	ERR_FAIL_COND_V(!rt, RID());
 	ERR_FAIL_COND_V(!rt, RID());

+ 5 - 1
servers/rendering/rasterizer_rd/rasterizer_storage_rd.h

@@ -989,6 +989,7 @@ private:
 		bool flags[RENDER_TARGET_FLAG_MAX];
 		bool flags[RENDER_TARGET_FLAG_MAX];
 
 
 		RID backbuffer; //used for effects
 		RID backbuffer; //used for effects
+		RID backbuffer_fb;
 		RID backbuffer_mipmap0;
 		RID backbuffer_mipmap0;
 
 
 		struct BackbufferMipmap {
 		struct BackbufferMipmap {
@@ -1916,7 +1917,9 @@ public:
 	void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value);
 	void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value);
 	bool render_target_was_used(RID p_render_target);
 	bool render_target_was_used(RID p_render_target);
 	void render_target_set_as_unused(RID p_render_target);
 	void render_target_set_as_unused(RID p_render_target);
-	void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region);
+	void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
+	void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);
+	void render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region);
 
 
 	RID render_target_get_back_buffer_uniform_set(RID p_render_target, RID p_base_shader);
 	RID render_target_get_back_buffer_uniform_set(RID p_render_target, RID p_base_shader);
 
 
@@ -1930,6 +1933,7 @@ public:
 	RID render_target_get_rd_framebuffer(RID p_render_target);
 	RID render_target_get_rd_framebuffer(RID p_render_target);
 	RID render_target_get_rd_texture(RID p_render_target);
 	RID render_target_get_rd_texture(RID p_render_target);
 	RID render_target_get_rd_backbuffer(RID p_render_target);
 	RID render_target_get_rd_backbuffer(RID p_render_target);
+	RID render_target_get_rd_backbuffer_framebuffer(RID p_render_target);
 
 
 	RID render_target_get_framebuffer_uniform_set(RID p_render_target);
 	RID render_target_get_framebuffer_uniform_set(RID p_render_target);
 	RID render_target_get_backbuffer_uniform_set(RID p_render_target);
 	RID render_target_get_backbuffer_uniform_set(RID p_render_target);

+ 17 - 11
servers/rendering/rasterizer_rd/shaders/copy.glsl

@@ -15,6 +15,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
 #define FLAG_FORCE_LUMINANCE (1 << 6)
 #define FLAG_FORCE_LUMINANCE (1 << 6)
 #define FLAG_COPY_ALL_SOURCE (1 << 7)
 #define FLAG_COPY_ALL_SOURCE (1 << 7)
 #define FLAG_HIGH_QUALITY_GLOW (1 << 8)
 #define FLAG_HIGH_QUALITY_GLOW (1 << 8)
+#define FLAG_ALPHA_TO_ONE (1 << 9)
 
 
 layout(push_constant, binding = 1, std430) uniform Params {
 layout(push_constant, binding = 1, std430) uniform Params {
 	ivec4 section;
 	ivec4 section;
@@ -35,6 +36,8 @@ layout(push_constant, binding = 1, std430) uniform Params {
 	float camera_z_far;
 	float camera_z_far;
 	float camera_z_near;
 	float camera_z_near;
 	uint pad2[2];
 	uint pad2[2];
+
+	vec4 set_color;
 }
 }
 params;
 params;
 
 
@@ -42,7 +45,7 @@ params;
 layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
 layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
 #elif defined(MODE_CUBEMAP_TO_PANORAMA)
 #elif defined(MODE_CUBEMAP_TO_PANORAMA)
 layout(set = 0, binding = 0) uniform samplerCube source_color;
 layout(set = 0, binding = 0) uniform samplerCube source_color;
-#else
+#elif !defined(MODE_SET_COLOR)
 layout(set = 0, binding = 0) uniform sampler2D source_color;
 layout(set = 0, binding = 0) uniform sampler2D source_color;
 #endif
 #endif
 
 
@@ -203,25 +206,24 @@ void main() {
 		}
 		}
 		color = textureLod(source_color, uv, 0.0);
 		color = textureLod(source_color, uv, 0.0);
 
 
-		if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {
-			color.rgb = vec3(max(max(color.r, color.g), color.b));
-		}
-		imageStore(dest_buffer, pos + params.target, color);
-
 	} else {
 	} else {
 		color = texelFetch(source_color, pos + params.section.xy, 0);
 		color = texelFetch(source_color, pos + params.section.xy, 0);
 
 
-		if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {
-			color.rgb = vec3(max(max(color.r, color.g), color.b));
-		}
-
 		if (bool(params.flags & FLAG_FLIP_Y)) {
 		if (bool(params.flags & FLAG_FLIP_Y)) {
 			pos.y = params.section.w - pos.y - 1;
 			pos.y = params.section.w - pos.y - 1;
 		}
 		}
+	}
 
 
-		imageStore(dest_buffer, pos + params.target, color);
+	if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {
+		color.rgb = vec3(max(max(color.r, color.g), color.b));
+	}
+
+	if (bool(params.flags & FLAG_ALPHA_TO_ONE)) {
+		color.a = 1.0;
 	}
 	}
 
 
+	imageStore(dest_buffer, pos + params.target, color);
+
 #endif
 #endif
 
 
 #ifdef MODE_SIMPLE_COPY_DEPTH
 #ifdef MODE_SIMPLE_COPY_DEPTH
@@ -270,4 +272,8 @@ void main() {
 #endif
 #endif
 	imageStore(dest_buffer, pos + params.target, color);
 	imageStore(dest_buffer, pos + params.target, color);
 #endif
 #endif
+
+#ifdef MODE_SET_COLOR
+	imageStore(dest_buffer, pos + params.target, params.set_color);
+#endif
 }
 }

+ 94 - 2
servers/rendering/rendering_server_canvas.cpp

@@ -167,8 +167,15 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
 		p_z = ci->z_index;
 		p_z = ci->z_index;
 	}
 	}
 
 
+	RasterizerCanvas::Item *canvas_group_from = nullptr;
+	bool use_canvas_group = ci->canvas_group != nullptr && (ci->canvas_group->fit_empty || ci->commands != nullptr);
+	if (use_canvas_group) {
+		int zidx = p_z - RS::CANVAS_ITEM_Z_MIN;
+		canvas_group_from = z_last_list[zidx];
+	}
+
 	for (int i = 0; i < child_item_count; i++) {
 	for (int i = 0; i < child_item_count; i++) {
-		if (!child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) {
+		if ((!child_items[i]->behind && !use_canvas_group) || (ci->sort_y && child_items[i]->sort_y)) {
 			continue;
 			continue;
 		}
 		}
 		if (ci->sort_y) {
 		if (ci->sort_y) {
@@ -182,6 +189,70 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
 		ci->copy_back_buffer->screen_rect = xform.xform(ci->copy_back_buffer->rect).clip(p_clip_rect);
 		ci->copy_back_buffer->screen_rect = xform.xform(ci->copy_back_buffer->rect).clip(p_clip_rect);
 	}
 	}
 
 
+	if (use_canvas_group) {
+		int zidx = p_z - RS::CANVAS_ITEM_Z_MIN;
+		if (canvas_group_from == nullptr) {
+			// no list before processing this item, means must put stuff in group from the beginning of list.
+			canvas_group_from = z_list[zidx];
+		} else {
+			// there was a list before processing, so begin group from this one.
+			canvas_group_from = canvas_group_from->next;
+		}
+
+		if (canvas_group_from) {
+			// Has a place to begin the group from!
+
+			//compute a global rect (in global coords) for children in the same z layer
+			Rect2 rect_accum;
+			RasterizerCanvas::Item *c = canvas_group_from;
+			while (c) {
+				if (c == canvas_group_from) {
+					rect_accum = c->global_rect_cache;
+				} else {
+					rect_accum = rect_accum.merge(c->global_rect_cache);
+				}
+
+				c = c->next;
+			}
+
+			// We have two choices now, if user has drawn something, we must assume users wants to draw the "mask", so compute the size based on this.
+			// If nothing has been drawn, we just take it over and draw it ourselves.
+			if (ci->canvas_group->fit_empty && (ci->commands == nullptr ||
+													   (ci->commands->next == nullptr && ci->commands->type == Item::Command::TYPE_RECT && (static_cast<Item::CommandRect *>(ci->commands)->flags & RasterizerCanvas::CANVAS_RECT_IS_GROUP)))) {
+				// No commands, or sole command is the one used to draw, so we (re)create the draw command.
+				ci->clear();
+
+				if (rect_accum == Rect2()) {
+					rect_accum.size = Size2(1, 1);
+				}
+
+				rect_accum = rect_accum.grow(ci->canvas_group->fit_margin);
+
+				//draw it?
+				RasterizerCanvas::Item::CommandRect *crect = ci->alloc_command<RasterizerCanvas::Item::CommandRect>();
+
+				crect->flags = RasterizerCanvas::CANVAS_RECT_IS_GROUP; // so we can recognize it later
+				crect->rect = xform.affine_inverse().xform(rect_accum);
+				crect->modulate = Color(1, 1, 1, 1);
+
+				//the global rect is used to do the copying, so update it
+				global_rect = rect_accum.grow(ci->canvas_group->clear_margin); //grow again by clear margin
+				global_rect.position += p_clip_rect.position;
+			} else {
+				global_rect.position -= p_clip_rect.position;
+
+				global_rect = global_rect.merge(rect_accum); //must use both rects for this
+				global_rect = global_rect.grow(ci->canvas_group->clear_margin); //grow by clear margin
+
+				global_rect.position += p_clip_rect.position;
+			}
+
+			// Very important that this is cleared after used in RasterizerCanvas to avoid
+			// potential crashes.
+			canvas_group_from->canvas_group_owner = ci;
+		}
+	}
+
 	if (ci->update_when_visible) {
 	if (ci->update_when_visible) {
 		RenderingServerRaster::redraw_request();
 		RenderingServerRaster::redraw_request();
 	}
 	}
@@ -211,7 +282,7 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
 	}
 	}
 
 
 	for (int i = 0; i < child_item_count; i++) {
 	for (int i = 0; i < child_item_count; i++) {
-		if (child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) {
+		if (child_items[i]->behind || use_canvas_group || (ci->sort_y && child_items[i]->sort_y)) {
 			continue;
 			continue;
 		}
 		}
 		if (ci->sort_y) {
 		if (ci->sort_y) {
@@ -935,6 +1006,27 @@ void RenderingServerCanvas::canvas_item_set_use_parent_material(RID p_item, bool
 	canvas_item->use_parent_material = p_enable;
 	canvas_item->use_parent_material = p_enable;
 }
 }
 
 
+void RenderingServerCanvas::canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin, bool p_fit_empty, float p_fit_margin, bool p_blur_mipmaps) {
+	Item *canvas_item = canvas_item_owner.getornull(p_item);
+	ERR_FAIL_COND(!canvas_item);
+
+	if (p_mode == RS::CANVAS_GROUP_MODE_DISABLED) {
+		if (canvas_item->canvas_group != nullptr) {
+			memdelete(canvas_item->canvas_group);
+			canvas_item->canvas_group = nullptr;
+		}
+	} else {
+		if (canvas_item->canvas_group == nullptr) {
+			canvas_item->canvas_group = memnew(RasterizerCanvas::Item::CanvasGroup);
+		}
+		canvas_item->canvas_group->mode = p_mode;
+		canvas_item->canvas_group->fit_empty = p_fit_empty;
+		canvas_item->canvas_group->fit_margin = p_fit_margin;
+		canvas_item->canvas_group->blur_mipmaps = p_blur_mipmaps;
+		canvas_item->canvas_group->clear_margin = p_clear_margin;
+	}
+}
+
 RID RenderingServerCanvas::canvas_light_create() {
 RID RenderingServerCanvas::canvas_light_create() {
 	RasterizerCanvas::Light *clight = memnew(RasterizerCanvas::Light);
 	RasterizerCanvas::Light *clight = memnew(RasterizerCanvas::Light);
 	clight->light_internal = RSG::canvas_render->light_create();
 	clight->light_internal = RSG::canvas_render->light_create();

+ 2 - 0
servers/rendering/rendering_server_canvas.h

@@ -216,6 +216,8 @@ public:
 
 
 	void canvas_item_set_use_parent_material(RID p_item, bool p_enable);
 	void canvas_item_set_use_parent_material(RID p_item, bool p_enable);
 
 
+	void canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false);
+
 	RID canvas_light_create();
 	RID canvas_light_create();
 	void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
 	void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
 	void canvas_light_set_enabled(RID p_light, bool p_enabled);
 	void canvas_light_set_enabled(RID p_light, bool p_enabled);

+ 2 - 0
servers/rendering/rendering_server_raster.h

@@ -743,6 +743,8 @@ public:
 
 
 	BIND2(canvas_item_set_use_parent_material, RID, bool)
 	BIND2(canvas_item_set_use_parent_material, RID, bool)
 
 
+	BIND6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
+
 	BIND0R(RID, canvas_light_create)
 	BIND0R(RID, canvas_light_create)
 	BIND2(canvas_light_attach_to_canvas, RID, RID)
 	BIND2(canvas_light_attach_to_canvas, RID, RID)
 	BIND2(canvas_light_set_enabled, RID, bool)
 	BIND2(canvas_light_set_enabled, RID, bool)

+ 2 - 0
servers/rendering/rendering_server_wrap_mt.h

@@ -642,6 +642,8 @@ public:
 
 
 	FUNC2(canvas_item_set_use_parent_material, RID, bool)
 	FUNC2(canvas_item_set_use_parent_material, RID, bool)
 
 
+	FUNC6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
+
 	FUNC0R(RID, canvas_light_create)
 	FUNC0R(RID, canvas_light_create)
 	FUNC2(canvas_light_attach_to_canvas, RID, RID)
 	FUNC2(canvas_light_attach_to_canvas, RID, RID)
 	FUNC2(canvas_light_set_enabled, RID, bool)
 	FUNC2(canvas_light_set_enabled, RID, bool)

+ 8 - 0
servers/rendering_server.h

@@ -1184,6 +1184,14 @@ public:
 
 
 	virtual void canvas_item_set_use_parent_material(RID p_item, bool p_enable) = 0;
 	virtual void canvas_item_set_use_parent_material(RID p_item, bool p_enable) = 0;
 
 
+	enum CanvasGroupMode {
+		CANVAS_GROUP_MODE_DISABLED,
+		CANVAS_GROUP_MODE_OPAQUE,
+		CANVAS_GROUP_MODE_TRANSPARENT,
+	};
+
+	virtual void canvas_item_set_canvas_group_mode(RID p_item, CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false) = 0;
+
 	virtual RID canvas_light_create() = 0;
 	virtual RID canvas_light_create() = 0;
 	virtual void canvas_light_attach_to_canvas(RID p_light, RID p_canvas) = 0;
 	virtual void canvas_light_attach_to_canvas(RID p_light, RID p_canvas) = 0;
 	virtual void canvas_light_set_enabled(RID p_light, bool p_enabled) = 0;
 	virtual void canvas_light_set_enabled(RID p_light, bool p_enabled) = 0;