Browse Source

Various fixes and documentation for CanvasGroup

Properly apply custom materials with CanvasGroups in the GLES3 backend

Properly blur backbuffer when using a partial rect in forward_plus and
gl_compatibility renderers

Properly set fit_margin when clear_margin is set

Fix shader error during backbuffer clear in mobile renderer
clayjohn 2 years ago
parent
commit
bdd4001ef0

+ 17 - 0
doc/classes/CanvasGroup.xml

@@ -5,16 +5,33 @@
 	</brief_description>
 	<description>
 		Child [CanvasItem] nodes of a [CanvasGroup] are drawn as a single object. It allows to e.g. draw overlapping translucent 2D nodes without blending (set [member CanvasItem.self_modulate] property of [CanvasGroup] to achieve this effect).
+		[b]Note:[/b] The [CanvasGroup] uses a custom shader to read from the backbuffer to draw its children. Assigning a [Material] to the [CanvasGroup] overrides the builtin shader. To duplicate the behavior of the builtin shader in a custom [Shader] use the following:
+		[codeblock]
+		shader_type canvas_item;
+
+		void fragment() {
+		    vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
+
+		    if (c.a &gt; 0.0001) {
+		        c.rgb /= c.a;
+		    }
+
+		    COLOR *= c;
+		}
+		[/codeblock]
 		[b]Note:[/b] Since [CanvasGroup] and [member CanvasItem.clip_children] both utilize the backbuffer, children of a [CanvasGroup] who have their [member CanvasItem.clip_children] set to anything other than [constant CanvasItem.CLIP_CHILDREN_DISABLED] will not function correctly.
 	</description>
 	<tutorials>
 	</tutorials>
 	<members>
 		<member name="clear_margin" type="float" setter="set_clear_margin" getter="get_clear_margin" default="10.0">
+			Sets the size of the margin used to expand the clearing rect of this [CanvasGroup]. This expands the area of the backbuffer that will be used by the [CanvasGroup]. A smaller margin will reduce the area of the backbuffer used which can increase performance, however if [member use_mipmaps] is enabled, a small margin may result in mipmap errors at the edge of the [CanvasGroup]. Accordingly, this should be left as small as possible, but should be increased if artifacts appear along the edges of the canvas group.
 		</member>
 		<member name="fit_margin" type="float" setter="set_fit_margin" getter="get_fit_margin" default="10.0">
+			Sets the size of a margin used to expand the drawable rect of this [CanvasGroup]. The size of the [CanvasGroup] is determined by fitting a rect around its children then expanding that rect by [member fit_margin]. This increases both the backbuffer area used and the area covered by the [CanvasGroup] both of which can reduce performance. This should be kept as small as possible and should only be expanded when an increased size is needed (e.g. for custom shader effects).
 		</member>
 		<member name="use_mipmaps" type="bool" setter="set_use_mipmaps" getter="is_using_mipmaps" default="false">
+			If [code]true[/code], calculates mipmaps for the backbuffer before drawing the [CanvasGroup] so that mipmaps can be used in a custom [ShaderMaterial] attached to the [CanvasGroup]. Generating mipmaps has a performance cost so this should not be enabled unless required.
 		</member>
 	</members>
 </class>

+ 2 - 2
drivers/gles3/effects/copy_effects.cpp

@@ -148,8 +148,8 @@ void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, con
 		dest_region.size.y = MAX(1, dest_region.size.y >> 1);
 		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[i % 2]);
 		glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
-		glBlitFramebuffer(source_region.position.x, source_region.position.y, source_region.size.x, source_region.size.y,
-				dest_region.position.x, dest_region.position.y, dest_region.size.x, dest_region.size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+		glBlitFramebuffer(source_region.position.x, source_region.position.y, source_region.position.x + source_region.size.x, source_region.position.y + source_region.size.y,
+				dest_region.position.x, dest_region.position.y, dest_region.position.x + dest_region.size.x, dest_region.position.y + dest_region.size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR);
 		glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[i % 2]);
 		source_region = dest_region;
 	}

+ 6 - 4
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -588,10 +588,12 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
 					material = default_clip_children_material;
 				}
 			} else {
-				if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) {
-					material = default_clip_children_material;
-				} else {
-					material = default_canvas_group_material;
+				if (material.is_null()) {
+					if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) {
+						material = default_clip_children_material;
+					} else {
+						material = default_canvas_group_material;
+					}
 				}
 			}
 		}

+ 1 - 1
scene/2d/canvas_group.cpp

@@ -47,7 +47,7 @@ void CanvasGroup::set_clear_margin(real_t 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);
+	RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, fit_margin, use_mipmaps);
 
 	queue_redraw();
 }

+ 5 - 3
servers/rendering/renderer_rd/effects/copy_effects.cpp

@@ -628,7 +628,7 @@ void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) {
 	RD::get_singleton()->draw_list_end();
 }
 
-void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst) {
+void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, const Size2i &p_size, bool p_8bit_dst) {
 	ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian blur with the mobile renderer.");
 
 	UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
@@ -640,8 +640,10 @@ void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Re
 
 	copy.push_constant.section[0] = p_region.position.x;
 	copy.push_constant.section[1] = p_region.position.y;
-	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.section[2] = p_size.width;
+	copy.push_constant.section[3] = p_size.height;
 
 	// setup our uniforms
 	RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);

+ 1 - 1
servers/rendering/renderer_rd/effects/copy_effects.h

@@ -331,7 +331,7 @@ public:
 	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_raster(RID p_source_texture, RID p_dest_framebuffer);
 
-	void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false);
+	void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, const Size2i &p_size, bool p_8bit_dst = false);
 	void gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_region, const Size2i &p_size);
 	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_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0);
 	void gaussian_glow_raster(RID p_source_rd_texture, RID p_half_texture, RID p_dest_texture, float p_luminance_multiplier, 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_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0);

+ 1 - 1
servers/rendering/renderer_rd/shaders/effects/copy.glsl

@@ -93,7 +93,7 @@ void main() {
 #ifdef MODE_GAUSSIAN_BLUR
 
 	// First pass copy texture into 16x16 local memory for every 8x8 thread block
-	vec2 quad_center_uv = clamp(vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.5) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
+	vec2 quad_center_uv = clamp(vec2(params.section.xy + gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.5) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
 	uint dest_index = gl_LocalInvocationID.x * 2 + gl_LocalInvocationID.y * 2 * 16;
 
 #ifdef MODE_GLOW

+ 2 - 0
servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl

@@ -88,6 +88,7 @@ layout(push_constant, std430) uniform Params {
 }
 params;
 
+#ifndef MODE_SET_COLOR
 #ifdef MULTIVIEW
 layout(location = 0) in vec3 uv_interp;
 #else
@@ -106,6 +107,7 @@ layout(set = 0, binding = 0) uniform sampler2D source_color;
 layout(set = 1, binding = 0) uniform sampler2D source_color2;
 #endif /* MODE_TWO_SOURCES */
 #endif /* MULTIVIEW */
+#endif /* !SET_COLOR */
 
 layout(location = 0) out vec4 frag_color;
 

+ 7 - 7
servers/rendering/renderer_rd/storage_rd/texture_storage.cpp

@@ -3140,13 +3140,13 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
 		region.position.y >>= 1;
 		region.size.x = MAX(1, region.size.x >> 1);
 		region.size.y = MAX(1, region.size.y >> 1);
+		texture_size.x = MAX(1, texture_size.x >> 1);
+		texture_size.y = MAX(1, texture_size.y >> 1);
 
 		RID mipmap = rt->backbuffer_mipmaps[i];
 		if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
-			copy_effects->gaussian_blur(prev_texture, mipmap, region, true);
+			copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true);
 		} else {
-			texture_size.x = MAX(1, texture_size.x >> 1);
-			texture_size.y = MAX(1, texture_size.y >> 1);
 			copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
 		}
 		prev_texture = mipmap;
@@ -3179,7 +3179,7 @@ void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const
 	if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
 		copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, true);
 	} else {
-		copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, true);
+		copy_effects->set_color_raster(rt->backbuffer_mipmap0, p_color, region);
 	}
 }
 
@@ -3213,14 +3213,14 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
 		region.position.y >>= 1;
 		region.size.x = MAX(1, region.size.x >> 1);
 		region.size.y = MAX(1, region.size.y >> 1);
+		texture_size.x = MAX(1, texture_size.x >> 1);
+		texture_size.y = MAX(1, texture_size.y >> 1);
 
 		RID mipmap = rt->backbuffer_mipmaps[i];
 
 		if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
-			copy_effects->gaussian_blur(prev_texture, mipmap, region, true);
+			copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true);
 		} else {
-			texture_size.x = MAX(1, texture_size.x >> 1);
-			texture_size.y = MAX(1, texture_size.y >> 1);
 			copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
 		}
 		prev_texture = mipmap;