From c4021fa656b7c00df68dab1b24c497040dea9bfb Mon Sep 17 00:00:00 2001
From: SlugFiller <5435495+SlugFiller@users.noreply.github.com>
Date: Fri, 10 Mar 2023 16:49:46 +0200
Subject: [PATCH] Cascaded canvas groups and canvas group shader revamp
---
doc/classes/CanvasGroup.xml | 15 +-
doc/classes/CanvasItem.xml | 20 +-
doc/classes/RenderingServer.xml | 4 +
doc/classes/VisualShaderNodeTexture.xml | 5 +-
.../VisualShaderNodeTextureParameter.xml | 5 +-
drivers/gles3/rasterizer_canvas_gles3.cpp | 280 +++++++-------
drivers/gles3/rasterizer_canvas_gles3.h | 10 +-
drivers/gles3/shaders/canvas.glsl | 35 +-
.../gles3/shaders/canvas_uniforms_inc.glsl | 1 +
drivers/gles3/storage/material_storage.cpp | 5 +
drivers/gles3/storage/material_storage.h | 2 +
drivers/gles3/storage/texture_storage.cpp | 281 ++++++++++++--
drivers/gles3/storage/texture_storage.h | 17 +
scene/main/canvas_item.cpp | 3 +-
scene/main/canvas_item.h | 9 +-
scene/resources/visual_shader_nodes.cpp | 35 +-
scene/resources/visual_shader_nodes.h | 2 +
servers/rendering/renderer_canvas_cull.cpp | 9 +-
servers/rendering/renderer_canvas_render.h | 3 +-
.../renderer_rd/renderer_canvas_render_rd.cpp | 363 +++++++++++-------
.../renderer_rd/renderer_canvas_render_rd.h | 26 +-
.../rendering/renderer_rd/shaders/canvas.glsl | 27 +-
.../shaders/canvas_uniforms_inc.glsl | 4 +-
.../storage_rd/material_storage.cpp | 2 +
.../storage_rd/texture_storage.cpp | 334 +++++++++++++++-
.../renderer_rd/storage_rd/texture_storage.h | 36 +-
servers/rendering/shader_compiler.cpp | 17 +-
servers/rendering/shader_compiler.h | 2 +
servers/rendering/shader_language.cpp | 17 +-
servers/rendering/shader_language.h | 2 +
servers/rendering_server.cpp | 1 +
servers/rendering_server.h | 2 +
32 files changed, 1204 insertions(+), 370 deletions(-)
diff --git a/doc/classes/CanvasGroup.xml b/doc/classes/CanvasGroup.xml
index 2792e593e396..0cd9a0e060cc 100644
--- a/doc/classes/CanvasGroup.xml
+++ b/doc/classes/CanvasGroup.xml
@@ -5,24 +5,19 @@
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:
+ By default [CanvasGroup] multiplies its [member CanvasItem.self_modulate] with the color of its children. It is possible to set a custom blending for the children using a custom [Shader] with a [code]sampler2D[/code] with [code]hint_mask_texture[/code]. Doing so disables the default operation.
+ The following example only uses the alpha from the children, overriding their color:
[codeblock]
shader_type canvas_item;
- render_mode unshaded;
- uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
+ uniform sampler2D mask_texture : hint_mask_texture, repeat_disable, filter_nearest;
void fragment() {
- vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0);
+ vec4 c = textureLod(mask_texture, SCREEN_UV, 0.0);
- if (c.a > 0.0001) {
- c.rgb /= c.a;
- }
-
- COLOR *= c;
+ COLOR.a *= c.a;
}
[/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.
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 207045b06546..6f26eafcbcb8 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -586,6 +586,21 @@
Allows the current node to clip child nodes, essentially acting as a mask.
+ When set to anything other than [constant CanvasItem.CLIP_CHILDREN_DISABLED] it is possible to set a custom blending for the masked children using a custom [Shader] with a [code]sampler2D[/code] with [code]hint_mask_texture[/code]. Doing so disables the default masking operation.
+ For example, the following multiplies the parent's color with that of its children:
+ [codeblock]
+ shader_type canvas_item;
+ render_mode blend_premul_alpha;
+
+ uniform sampler2D mask_texture : hint_mask_texture, repeat_disable, filter_nearest;
+
+ void fragment() {
+ vec4 c = textureLod(mask_texture, SCREEN_UV, 0.0);
+
+ COLOR.rgb *= COLOR.a;
+ COLOR *= c;
+ }
+ [/codeblock]
The rendering layers in which this [CanvasItem] responds to [Light2D] nodes.
@@ -732,7 +747,10 @@
Parent is used for clipping child, but parent is also drawn underneath child as normal before clipping child to its visible area.
-
+
+ Child cuts hole into the parent, causing it to be transparent where the child is opaque. Child is not drawn.
+
+
Represents the size of the [enum ClipChildrenMode] enum.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 3c9f0fc7aff4..0212a3a352bc 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -5440,6 +5440,10 @@
Parent is used for clipping child, but parent is also drawn underneath child as normal before clipping child to its visible area.
+ The parent's color and alpha are multiplied with that of the child. An parent with no draw commands is interpreted as a fullscreen rectangle drawn in the self-modulation color.
+
+
+ Child cuts hole into the parent, causing it to be transparent where the child is opaque. Child is not drawn.
2D point light (see [PointLight2D]).
diff --git a/doc/classes/VisualShaderNodeTexture.xml b/doc/classes/VisualShaderNodeTexture.xml
index eda09573adbd..5ba19e06c1eb 100644
--- a/doc/classes/VisualShaderNodeTexture.xml
+++ b/doc/classes/VisualShaderNodeTexture.xml
@@ -44,7 +44,10 @@
Use the roughness buffer captured during the depth prepass. Only available when the normal-roughness buffer is available (i.e. in spatial shaders and in the forward_plus renderer).
-
+
+ For [CanvasGroup] or [CanvasItem] with [member CanvasItem.clip_children] set to anything other than [constant CanvasItem.CLIP_CHILDREN_DISABLED], use a texture made from rendering all child items. If used, the default masking operation is disabled.
+
+
Represents the size of the [enum Source] enum.
diff --git a/doc/classes/VisualShaderNodeTextureParameter.xml b/doc/classes/VisualShaderNodeTextureParameter.xml
index aa64016746f0..ef1f87a15834 100644
--- a/doc/classes/VisualShaderNodeTextureParameter.xml
+++ b/doc/classes/VisualShaderNodeTextureParameter.xml
@@ -105,7 +105,10 @@
The texture source is the normal-roughness buffer from the depth prepass.
-
+
+ For [CanvasGroup] or [CanvasItem] with [member CanvasItem.clip_children] set to anything other than [constant CanvasItem.CLIP_CHILDREN_DISABLED], the texture source is made from rendering all child items. If used, the default masking operation is disabled.
+
+
Represents the size of the [enum TextureSource] enum.
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 941b1a1b2805..d56d983b3183 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -382,7 +382,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
- glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 5);
+ glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 6);
glBindTexture(GL_TEXTURE_2D, texture_storage->render_target_get_sdf_texture(p_to_render_target));
{
@@ -395,9 +395,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
r_sdf_used = false;
int item_count = 0;
- bool backbuffer_cleared = false;
bool time_used = false;
- bool material_screen_texture_cached = false;
+ bool uses_mask_texture = false;
+ bool uses_mask_texture_mipmaps = false;
+ uint32_t material_screen_texture_cached = -1;
bool material_screen_texture_mipmaps_cached = false;
Rect2 back_buffer_rect;
bool backbuffer_copy = false;
@@ -405,13 +406,17 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
bool update_skeletons = false;
Item *ci = p_item_list;
- Item *canvas_group_owner = nullptr;
+ const bool alias_screen_to_mask = true; // FIXME: Control this with a project setting?
+ uint32_t canvas_group_level = 0;
+ uint32_t canvas_group_max_level = 0;
bool skip_item = false;
state.last_item_index = 0;
while (ci) {
- if (ci->copy_back_buffer && canvas_group_owner == nullptr) {
+ bool uses_mask_texture_explicit = false;
+
+ if (ci->copy_back_buffer) {
backbuffer_copy = true;
if (ci->copy_back_buffer->full) {
@@ -426,8 +431,8 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
if (material.is_valid()) {
GLES3::CanvasMaterialData *md = static_cast(material_storage->material_get_data(material, RS::SHADER_CANVAS_ITEM));
if (md && md->shader_data->valid) {
- if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) {
- if (!material_screen_texture_cached) {
+ if (md->shader_data->uses_screen_texture) {
+ if (material_screen_texture_cached != ci->canvas_group_level) {
backbuffer_copy = true;
back_buffer_rect = Rect2();
backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps;
@@ -442,6 +447,28 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
if (md->shader_data->uses_time) {
time_used = true;
}
+ if (ci->canvas_group != nullptr) {
+ if (md->shader_data->uses_mask_texture) {
+ uses_mask_texture_explicit = uses_mask_texture = true;
+ if (md->shader_data->uses_mask_texture_mipmaps) {
+ uses_mask_texture_mipmaps = true;
+ }
+ } else {
+ if (alias_screen_to_mask && md->shader_data->uses_screen_texture) {
+ uses_mask_texture = true;
+ }
+ if (md->shader_data->uses_mask_texture_mipmaps || (alias_screen_to_mask && md->shader_data->uses_screen_texture_mipmaps)) {
+ uses_mask_texture_mipmaps = true;
+ }
+ }
+ } else if (md->shader_data->uses_mask_texture) {
+ // An item that hasn't defined a canvas group receives an empty mask
+ texture_storage->render_target_clear_canvas_group(p_to_render_target, canvas_group_level, false);
+ if (md->shader_data->uses_mask_texture_mipmaps) {
+ // Clear the mipmaps too, if needed
+ texture_storage->render_target_gen_canvas_group_mipmaps(p_to_render_target, ci->global_rect_cache, canvas_group_level);
+ }
+ }
}
}
@@ -461,59 +488,71 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
}
- if (ci->canvas_group_owner != nullptr) {
- if (canvas_group_owner == nullptr) {
- if (update_skeletons) {
- mesh_storage->update_mesh_instances();
- update_skeletons = false;
- }
- // Canvas group begins here, render until before this item
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
- item_count = 0;
-
- if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) {
- Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
- texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
- if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- ci->canvas_group_owner->use_canvas_group = false;
- items[item_count++] = ci->canvas_group_owner;
- }
- } else if (!backbuffer_cleared) {
- texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
- backbuffer_cleared = true;
- }
-
- backbuffer_copy = false;
- canvas_group_owner = ci->canvas_group_owner; //continue until owner found
+ if (ci->canvas_group_level > canvas_group_level) {
+ if (update_skeletons) {
+ mesh_storage->update_mesh_instances();
+ update_skeletons = false;
}
+ // Canvas group begins here, render until before this item
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, false, r_render_info);
+ item_count = 0;
- ci->canvas_group_owner = nullptr; //must be cleared
- }
+ while (ci->canvas_group_level > canvas_group_level) {
+ texture_storage->render_target_clear_canvas_group(p_to_render_target, canvas_group_level);
+ canvas_group_level++;
+ }
- if (canvas_group_owner == nullptr && ci->canvas_group != nullptr && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- skip_item = true;
+ if (canvas_group_max_level < canvas_group_level) {
+ canvas_group_max_level = canvas_group_level;
+ }
}
- if (ci == canvas_group_owner) {
+ if (ci->canvas_group_level < canvas_group_level) {
if (update_skeletons) {
mesh_storage->update_mesh_instances();
update_skeletons = false;
}
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, false, r_render_info);
item_count = 0;
- if (ci->canvas_group->blur_mipmaps) {
- texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, ci->global_rect_cache);
+ if (material_screen_texture_cached > ci->canvas_group_level) {
+ // A backbuffer from a canvas group that ended is no longer valid
+ material_screen_texture_cached = -1;
}
- canvas_group_owner = nullptr;
- // Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it.
- backbuffer_cleared = false;
+ // Every reduction in canvas group level must be accompanied by a canvas group parent being drawn
+ DEV_ASSERT(ci->canvas_group != nullptr);
+ DEV_ASSERT(canvas_group_level - 1 == ci->canvas_group_level);
+ canvas_group_level = ci->canvas_group_level;
+
+ if (ci->canvas_group->blur_mipmaps || uses_mask_texture_mipmaps) {
+ texture_storage->render_target_gen_canvas_group_mipmaps(p_to_render_target, ci->global_rect_cache, canvas_group_level);
+ }
+
+ // The mask buffer is dirty now and needs to be cleared if another element tries to use it
+ texture_storage->render_target_set_canvas_group_needs_clear(p_to_render_target, canvas_group_level);
+ uses_mask_texture = false;
+ uses_mask_texture_mipmaps = false;
// Tell the renderer to paint this as a canvas group
ci->use_canvas_group = true;
} else {
ci->use_canvas_group = false;
+ if (ci->canvas_group != nullptr) {
+ if (uses_mask_texture) {
+ // A canvas group with no children, and a custom shader. Prevent it from receiving a dirty mask texture
+ texture_storage->render_target_clear_canvas_group(p_to_render_target, canvas_group_level, false);
+ if (uses_mask_texture_mipmaps) {
+ // Clear the mipmaps too, if needed
+ texture_storage->render_target_gen_canvas_group_mipmaps(p_to_render_target, ci->global_rect_cache, canvas_group_level);
+ }
+ uses_mask_texture = false;
+ uses_mask_texture_mipmaps = false;
+ } else if (ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_SUBTRACT) {
+ // Skip CanvasGroup or clip-only parent with no children if it isn't using a custom blend for the mask
+ skip_item = true;
+ }
+ }
}
if (backbuffer_copy) {
@@ -523,13 +562,17 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
//render anything pending, including clearing if no items
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, false, r_render_info);
item_count = 0;
- texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
+ if (canvas_group_level > 0) {
+ texture_storage->render_target_copy_to_back_buffer_from_canvas_group(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps, canvas_group_level - 1);
+ } else {
+ texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
+ }
backbuffer_copy = false;
- material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies.
+ material_screen_texture_cached = ci->canvas_group_level; // After a backbuffer copy, screen texture makes no further copies.
material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps;
backbuffer_gen_mipmaps = false;
}
@@ -548,12 +591,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
items[item_count++] = ci;
}
- if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
+ if (!ci->next || item_count == MAX_RENDER_ITEMS - 1 || (alias_screen_to_mask && ci->use_canvas_group && !uses_mask_texture_explicit)) {
if (update_skeletons) {
mesh_storage->update_mesh_instances();
update_skeletons = false;
}
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, alias_screen_to_mask && ci->use_canvas_group && !uses_mask_texture_explicit, r_render_info);
//then reset
item_count = 0;
}
@@ -561,6 +604,8 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
ci = ci->next;
}
+ texture_storage->render_target_set_canvas_groups_used(p_to_render_target, canvas_group_max_level);
+
if (time_used) {
RenderingServerDefault::redraw_request();
}
@@ -573,10 +618,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
state.current_instance_buffer_index = 0;
}
-void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) {
+void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask, RenderingMethod::RenderInfo *r_render_info) {
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
- canvas_begin(p_to_render_target, p_to_backbuffer);
+ canvas_begin(p_to_render_target, p_canvas_group_level, p_alias_screen_to_mask);
if (p_item_count <= 0) {
// Nothing to draw, just call canvas_begin() to clear the render target and return.
@@ -606,19 +651,6 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
- if (ci->use_canvas_group) {
- if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- material = default_clip_children_material;
- } else {
- 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;
- }
- }
- }
- }
if (material != state.canvas_instance_batches[state.current_batch_index].material) {
_new_batch(batch_broken);
@@ -641,6 +673,11 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
}
+ if (shader_data_cache && (shader_data_cache->uses_mask_texture || (p_alias_screen_to_mask && shader_data_cache->uses_screen_texture))) {
+ // If the shader uses the mask, disable the default handling
+ ci->use_canvas_group = false;
+ }
+
GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX;
if (!ci->repeat_size.x && !ci->repeat_size.y) {
@@ -710,6 +747,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
uint64_t specialization = 0;
specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled);
specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1;
+ specialization |= uint64_t(state.canvas_instance_batches[i].use_mask) << 3;
RID shader_version = data.canvas_shader_default_version;
if (material_data) {
@@ -868,10 +906,38 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
}
bool lights_disabled = light_count == 0 && !state.using_directional_lights;
+ bool use_mask = false;
+ if (p_item->use_canvas_group) {
+ use_mask = true;
+ switch (p_item->canvas_group->mode) {
+ case RS::CANVAS_GROUP_MODE_CLIP_ONLY: {
+ lights_disabled = false;
+ base_flags |= 1 << FLAGS_MASK_MODE_SHIFT;
+
+ } break;
+ case RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW: {
+ base_flags |= 2 << FLAGS_MASK_MODE_SHIFT;
+
+ } break;
+ case RS::CANVAS_GROUP_MODE_SUBTRACT: {
+ base_flags |= 3 << FLAGS_MASK_MODE_SHIFT;
- if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) {
+ } break;
+ case RS::CANVAS_GROUP_MODE_TRANSPARENT: {
+ lights_disabled = false;
+
+ } break;
+ case RS::CANVAS_GROUP_MODE_DISABLED: {
+ use_mask = false;
+
+ } break;
+ }
+ }
+
+ if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled || use_mask != state.canvas_instance_batches[state.current_batch_index].use_mask) {
_new_batch(r_batch_broken);
state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled;
+ state.canvas_instance_batches[state.current_batch_index].use_mask = use_mask;
}
const Item::Command *c = p_item->commands;
@@ -2170,24 +2236,32 @@ bool RasterizerCanvasGLES3::free(RID p_rid) {
void RasterizerCanvasGLES3::update() {
}
-void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backbuffer) {
+void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
GLES3::RenderTarget *render_target = texture_storage->get_render_target(p_to_render_target);
- if (p_to_backbuffer) {
- glBindFramebuffer(GL_FRAMEBUFFER, render_target->backbuffer_fbo);
- glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
- GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE));
- glBindTexture(GL_TEXTURE_2D, tex->tex_id);
+ if (p_canvas_group_level) {
+ glBindFramebuffer(GL_FRAMEBUFFER, render_target->canvas_group_levels[p_canvas_group_level - 1].framebuffer);
} else {
glBindFramebuffer(GL_FRAMEBUFFER, render_target->fbo);
- glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
+ }
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
+ if (p_alias_screen_to_mask && p_canvas_group_level < render_target->canvas_group_levels.size()) {
+ glBindTexture(GL_TEXTURE_2D, render_target->canvas_group_levels[p_canvas_group_level].texture);
+ } else {
glBindTexture(GL_TEXTURE_2D, render_target->backbuffer);
}
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5);
+ if (p_canvas_group_level < render_target->canvas_group_levels.size()) {
+ glBindTexture(GL_TEXTURE_2D, render_target->canvas_group_levels[p_canvas_group_level].texture);
+ } else {
+ GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_TRANSPARENT));
+ glBindTexture(GL_TEXTURE_2D, tex->tex_id);
+ }
- if (render_target->is_transparent || p_to_backbuffer) {
+ if (render_target->is_transparent || p_canvas_group_level) {
state.transparent_render_target = true;
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
@@ -2269,11 +2343,11 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe
GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map);
if (!normal_map) {
- glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_NORMAL));
glBindTexture(GL_TEXTURE_2D, tex->tex_id);
} else {
- glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
glBindTexture(GL_TEXTURE_2D, normal_map->tex_id);
normal_map->gl_set_filter(filter);
normal_map->gl_set_repeat(repeat);
@@ -2285,11 +2359,11 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe
GLES3::Texture *specular_map = texture_storage->get_texture(ct->specular);
if (!specular_map) {
- glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE));
glBindTexture(GL_TEXTURE_2D, tex->tex_id);
} else {
- glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
glBindTexture(GL_TEXTURE_2D, specular_map->tex_id);
specular_map->gl_set_filter(filter);
specular_map->gl_set_repeat(repeat);
@@ -2615,7 +2689,6 @@ RasterizerCanvasGLES3 *RasterizerCanvasGLES3::get_singleton() {
RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
singleton = this;
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
- GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0);
@@ -2802,57 +2875,6 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
shadow_render.shader.initialize();
shadow_render.shader_version = shadow_render.shader.version_create();
- {
- default_canvas_group_shader = material_storage->shader_allocate();
- material_storage->shader_initialize(default_canvas_group_shader);
-
- material_storage->shader_set_code(default_canvas_group_shader, R"(
-// Default CanvasGroup shader.
-
-shader_type canvas_item;
-render_mode unshaded;
-
-uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
-
-void fragment() {
- vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0);
-
- if (c.a > 0.0001) {
- c.rgb /= c.a;
- }
-
- COLOR *= c;
-}
-)");
- default_canvas_group_material = material_storage->material_allocate();
- material_storage->material_initialize(default_canvas_group_material);
-
- material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader);
- }
-
- {
- default_clip_children_shader = material_storage->shader_allocate();
- material_storage->shader_initialize(default_clip_children_shader);
-
- material_storage->shader_set_code(default_clip_children_shader, R"(
-// Default clip children shader.
-
-shader_type canvas_item;
-render_mode unshaded;
-
-uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
-
-void fragment() {
- vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0);
- COLOR.rgb = c.rgb;
-}
-)");
- default_clip_children_material = material_storage->material_allocate();
- material_storage->material_initialize(default_clip_children_material);
-
- material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader);
- }
-
default_canvas_texture = texture_storage->canvas_texture_allocate();
texture_storage->canvas_texture_initialize(default_canvas_texture);
@@ -2865,10 +2887,6 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() {
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
material_storage->shaders.canvas_shader.version_free(data.canvas_shader_default_version);
shadow_render.shader.version_free(shadow_render.shader_version);
- material_storage->material_free(default_canvas_group_material);
- material_storage->shader_free(default_canvas_group_shader);
- material_storage->material_free(default_clip_children_material);
- material_storage->shader_free(default_clip_children_shader);
singleton = nullptr;
glDeleteBuffers(1, &data.canvas_quad_vertices);
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 46ed479a3d6e..8962797453d7 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -68,6 +68,7 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
FLAGS_LIGHT_COUNT_SHIFT = 20,
+ FLAGS_MASK_MODE_SHIFT = 24,
FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27),
@@ -281,6 +282,7 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
uint32_t primitive_points = 0;
bool lights_disabled = false;
+ bool use_mask = false;
};
// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
@@ -328,14 +330,10 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
Item *items[MAX_RENDER_ITEMS];
RID default_canvas_texture;
- RID default_canvas_group_material;
- RID default_canvas_group_shader;
- RID default_clip_children_material;
- RID default_clip_children_shader;
typedef void Texture;
- void canvas_begin(RID p_to_render_target, bool p_to_backbuffer);
+ void canvas_begin(RID p_to_render_target, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask);
//virtual void draw_window_margins(int *black_margin, RID *black_image) override;
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
@@ -361,7 +359,7 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size);
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
- void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
+ void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_offset);
void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr);
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index e3582307473d..2c16caa366e4 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -12,6 +12,7 @@ mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
DISABLE_LIGHTING = true
USE_RGBA_SHADOWS = false
SINGLE_INSTANCE = false
+USE_MASK = false
#[vertex]
@@ -331,9 +332,10 @@ uniform sampler2D atlas_texture; //texunit:-2
uniform sampler2D shadow_atlas_texture; //texunit:-3
#endif // DISABLE_LIGHTING
uniform sampler2D color_buffer; //texunit:-4
-uniform sampler2D sdf_texture; //texunit:-5
-uniform sampler2D normal_texture; //texunit:-6
-uniform sampler2D specular_texture; //texunit:-7
+uniform sampler2D mask_buffer; //texunit:-5
+uniform sampler2D sdf_texture; //texunit:-6
+uniform sampler2D normal_texture; //texunit:-7
+uniform sampler2D specular_texture; //texunit:-8
uniform sampler2D color_texture; //texunit:0
@@ -662,7 +664,7 @@ void main() {
specular_shininess = vec4(1.0);
}
-#if defined(SCREEN_UV_USED)
+#if defined(SCREEN_UV_USED) || defined(USE_MASK)
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
#else
vec2 screen_uv = vec2(0.0);
@@ -853,5 +855,30 @@ void main() {
color.a *= light_only_alpha;
#endif
+#if defined(USE_MASK)
+ {
+ uint mask_mode = (read_draw_data_flags >> uint(FLAGS_MASK_MODE_SHIFT)) & uint(3);
+ vec4 mask = textureLod(mask_buffer, screen_uv, 0.0);
+
+ if (mask_mode == uint(0)) {
+ if (mask.a > 0.0001) {
+ mask.rgb /= mask.a;
+ }
+ color *= mask;
+ } else if (mask_mode == uint(1)) {
+ if (mask.a > 0.0001) {
+ mask.rgb /= mask.a;
+ }
+ mask.a *= color.a;
+ color = mask;
+ } else if (mask_mode == uint(2)) {
+ color.rgb *= 1.0 - mask.a;
+ color.rgb += mask.rgb;
+ } else if (mask_mode == uint(3)) {
+ color.a *= 1.0 - mask.a;
+ }
+ }
+#endif
+
frag_color = color;
}
diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
index f6ad2b730ab8..92786140a89e 100644
--- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl
+++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
@@ -19,6 +19,7 @@
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
#define FLAGS_LIGHT_COUNT_SHIFT 20
+#define FLAGS_MASK_MODE_SHIFT 24
#define FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 26)
#define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27)
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 21790d29b5de..c06569f627ce 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -840,6 +840,7 @@ void MaterialData::update_textures(const HashMap &p_paramet
Vector textures;
if (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
+ p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_MASK_TEXTURE ||
p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
continue;
@@ -2542,6 +2543,8 @@ void CanvasShaderData::set_code(const String &p_code) {
uses_screen_texture = false;
uses_screen_texture_mipmaps = false;
+ uses_mask_texture = false;
+ uses_mask_texture_mipmaps = false;
uses_sdf = false;
uses_time = false;
uses_custom0 = false;
@@ -2584,6 +2587,8 @@ void CanvasShaderData::set_code(const String &p_code) {
blend_mode = BlendMode(blend_modei);
uses_screen_texture = gen_code.uses_screen_texture;
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
+ uses_mask_texture = gen_code.uses_mask_texture;
+ uses_mask_texture_mipmaps = gen_code.uses_mask_texture_mipmaps;
#if 0
print_line("**compiling shader:");
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index 392ebcc570d9..45b48c895b98 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -165,6 +165,8 @@ struct CanvasShaderData : public ShaderData {
bool uses_screen_texture;
bool uses_screen_texture_mipmaps;
+ bool uses_mask_texture;
+ bool uses_mask_texture_mipmaps;
bool uses_sdf;
bool uses_time;
bool uses_custom0;
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 2dcf6239958d..5ec9dea06b6c 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -2140,58 +2140,58 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
}
void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) {
- ERR_FAIL_COND_MSG(rt->backbuffer_fbo != 0, "Cannot allocate RenderTarget backbuffer: already initialized.");
- ERR_FAIL_COND(rt->direct_to_screen);
+ // Caller should already check both of these, and assume there's a valid backbuffer on return
+ DEV_ASSERT(rt->backbuffer_fbo == 0);
+ DEV_ASSERT(!rt->direct_to_screen);
// Allocate mipmap chains for full screen blur
// Limit mipmaps so smallest is 32x32 to avoid unnecessary framebuffer switches
int count = MAX(1, Image::get_image_required_mipmaps(rt->size.x, rt->size.y, Image::FORMAT_RGBA8) - 4);
- if (rt->size.x > 40 && rt->size.y > 40) {
- GLsizei width = rt->size.x;
- GLsizei height = rt->size.y;
- rt->mipmap_count = count;
+ GLsizei width = rt->size.x;
+ GLsizei height = rt->size.y;
- glGenTextures(1, &rt->backbuffer);
- glBindTexture(GL_TEXTURE_2D, rt->backbuffer);
- uint32_t texture_size_bytes = 0;
-
- for (int l = 0; l < count; l++) {
- texture_size_bytes += width * height * 4;
- glTexImage2D(GL_TEXTURE_2D, l, rt->color_internal_format, width, height, 0, rt->color_format, rt->color_type, nullptr);
- width = MAX(1, (width / 2));
- height = MAX(1, (height / 2));
- }
+ rt->mipmap_count = count;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
+ glGenTextures(1, &rt->backbuffer);
+ glBindTexture(GL_TEXTURE_2D, rt->backbuffer);
+ uint32_t texture_size_bytes = 0;
- glGenFramebuffers(1, &rt->backbuffer_fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
+ for (int l = 0; l < count; l++) {
+ texture_size_bytes += width * height * 4;
+ glTexImage2D(GL_TEXTURE_2D, l, rt->color_internal_format, width, height, 0, rt->color_format, rt->color_type, nullptr);
+ width = MAX(1, (width / 2));
+ height = MAX(1, (height / 2));
+ }
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- WARN_PRINT_ONCE("Cannot allocate mipmaps for canvas screen blur. Status: " + get_framebuffer_error(status));
- glBindFramebuffer(GL_FRAMEBUFFER, system_fbo);
- return;
- }
- GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer, texture_size_bytes, "Render target backbuffer color texture");
+ glGenFramebuffers(1, &rt->backbuffer_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
- // Initialize all levels to clear black.
- for (int j = 0; j < count; j++) {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, j);
- glClearColor(0.0, 0.0, 0.0, 0.0);
- glClear(GL_COLOR_BUFFER_BIT);
- }
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0);
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ WARN_PRINT_ONCE("Cannot allocate mipmaps for canvas screen blur. Status: " + get_framebuffer_error(status));
+ glBindFramebuffer(GL_FRAMEBUFFER, system_fbo);
+ return;
+ }
+ GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer, texture_size_bytes, "Render target backbuffer color texture");
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ // Initialize all levels to clear black.
+ for (int j = 0; j < count; j++) {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, j);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
}
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) {
if (rt->backbuffer != 0 && rt->backbuffer_depth != 0) {
@@ -2253,6 +2253,65 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s
}
}
}
+
+bool TextureStorage::_create_render_target_canvas_group(RenderTarget *rt) {
+ ERR_FAIL_COND_V(rt->direct_to_screen, false);
+ RenderTarget::CanvasGroupLevel canvas_group_level;
+ // Allocate mipmap chains for full screen blur
+ // Limit mipmaps so smallest is 32x32 to avoid unnecessary framebuffer switches
+ int count = MAX(1, Image::get_image_required_mipmaps(rt->size.x, rt->size.y, Image::FORMAT_RGBA8) - 4);
+
+ GLsizei width = rt->size.x;
+ GLsizei height = rt->size.y;
+
+ canvas_group_level.mipmap_count = count;
+
+ glGenTextures(1, &canvas_group_level.texture);
+ glBindTexture(GL_TEXTURE_2D, canvas_group_level.texture);
+ uint32_t texture_size_bytes = 0;
+
+ for (int l = 0; l < count; l++) {
+ texture_size_bytes += width * height * 4;
+ glTexImage2D(GL_TEXTURE_2D, l, rt->color_internal_format, width, height, 0, rt->color_format, rt->color_type, nullptr);
+ width = MAX(1, (width / 2));
+ height = MAX(1, (height / 2));
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
+
+ glGenFramebuffers(1, &canvas_group_level.framebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, canvas_group_level.framebuffer);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas_group_level.texture, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ WARN_PRINT_ONCE("Cannot allocate mipmaps for canvas screen blur. Status: " + get_framebuffer_error(status));
+ glBindFramebuffer(GL_FRAMEBUFFER, system_fbo);
+ return false;
+ }
+ GLES3::Utilities::get_singleton()->texture_allocated_data(canvas_group_level.texture, texture_size_bytes, "Render target backbuffer color texture");
+
+ // Initialize all levels to transparent
+ for (int j = 0; j < count; j++) {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas_group_level.texture, j);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas_group_level.texture, 0);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ rt->canvas_group_levels.push_back(canvas_group_level);
+
+ return true;
+}
+
void TextureStorage::_clear_render_target(RenderTarget *rt) {
// there is nothing else to clear when DIRECT_TO_SCREEN is used
if (rt->direct_to_screen) {
@@ -2326,6 +2385,11 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
GLES3::Utilities::get_singleton()->texture_free_data(rt->backbuffer_depth);
rt->backbuffer_depth = 0;
}
+ for (uint32_t i = 0; i < rt->canvas_group_levels.size(); i++) {
+ glDeleteFramebuffers(1, &rt->canvas_group_levels[i].framebuffer);
+ GLES3::Utilities::get_singleton()->texture_free_data(rt->canvas_group_levels[i].texture);
+ }
+ rt->canvas_group_levels.clear();
_render_target_clear_sdf(rt);
}
@@ -3028,6 +3092,43 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
glEnable(GL_BLEND); // 2D starts with blend enabled.
}
+void TextureStorage::render_target_copy_to_back_buffer_from_canvas_group(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+ ERR_FAIL_COND(rt->direct_to_screen);
+
+ if (rt->backbuffer_fbo == 0) {
+ _create_render_target_backbuffer(rt);
+ }
+
+ ERR_FAIL_COND(p_canvas_group_level >= rt->canvas_group_levels.size());
+ const RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ Rect2i region;
+ if (p_region == Rect2i()) {
+ region.size = rt->size;
+ } else {
+ region = Rect2i(Size2i(), rt->size).intersection(p_region);
+ if (region.size == Size2i()) {
+ return; //nothing to do
+ }
+ }
+
+ glDisable(GL_BLEND);
+ //single texture copy for backbuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, canvas_group_level.texture);
+ GLES3::CopyEffects::get_singleton()->copy_screen();
+
+ if (p_gen_mipmaps) {
+ GLES3::CopyEffects::get_singleton()->bilinear_blur(rt->backbuffer, rt->mipmap_count, region);
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
+ }
+
+ glEnable(GL_BLEND); // 2D almost always uses blend.
+}
+
void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
@@ -3056,6 +3157,7 @@ void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const
void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
+ ERR_FAIL_COND(rt->direct_to_screen);
if (rt->backbuffer_fbo == 0) {
_create_render_target_backbuffer(rt);
@@ -3077,4 +3179,105 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
}
+void TextureStorage::render_target_clear_canvas_group(RID p_render_target, uint32_t p_canvas_group_level, bool p_allow_create) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+ ERR_FAIL_COND(rt->direct_to_screen);
+
+ if (!p_allow_create && p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ // This is not an error state.
+ // This simply indicates that a blank transparent texture should be used instead.
+ return;
+ }
+
+ while (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ if (!_create_render_target_canvas_group(rt)) {
+ return;
+ }
+ }
+ RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ if (!canvas_group_level.clear_needed) {
+ // Previously cleared, and no new items drawn
+ return;
+ }
+ canvas_group_level.clear_needed = false;
+ canvas_group_level.mipmaps_generated = false;
+
+ // Just do a full screen clear;
+ glBindFramebuffer(GL_FRAMEBUFFER, canvas_group_level.framebuffer);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void TextureStorage::render_target_gen_canvas_group_mipmaps(RID p_render_target, const Rect2i &p_region, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+ ERR_FAIL_COND(rt->direct_to_screen);
+
+ if (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ // Let the caller use the default transparent. It doesn't need mipmaps
+ return;
+ }
+ RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ if (canvas_group_level.mipmaps_generated) {
+ // Cached mipmaps exist
+ return;
+ }
+ canvas_group_level.mipmaps_generated = true;
+
+ Rect2i region;
+ if (p_region == Rect2i()) {
+ region.size = rt->size;
+ } else {
+ region = Rect2i(Size2i(), rt->size).intersection(p_region);
+ if (region.size == Size2i()) {
+ return; //nothing to do
+ }
+ }
+
+ GLES3::CopyEffects::get_singleton()->bilinear_blur(canvas_group_level.texture, canvas_group_level.mipmap_count, region);
+ glBindFramebuffer(GL_FRAMEBUFFER, canvas_group_level.framebuffer);
+}
+
+void TextureStorage::render_target_set_canvas_group_needs_clear(RID p_render_target, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ if (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ // Transparent default doesn't need to be any more clear
+ return;
+ }
+ RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ canvas_group_level.clear_needed = true;
+}
+
+void TextureStorage::render_target_set_canvas_groups_used(RID p_render_target, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ uint64_t last_used = OS::get_singleton()->get_ticks_usec();
+
+ uint32_t i;
+ for (i = 0; i < p_canvas_group_level; i++) {
+ rt->canvas_group_levels[i].last_used = last_used;
+ }
+ for (; i < rt->canvas_group_levels.size(); i++) {
+ if (last_used - rt->canvas_group_levels[i].last_used > 2000000L) {
+ // Release levels that haven't been used for more than 2 seconds
+ break;
+ }
+ }
+ uint32_t trunc = i;
+ for (; i < rt->canvas_group_levels.size(); i++) {
+ glDeleteFramebuffers(1, &rt->canvas_group_levels[i].framebuffer);
+ glDeleteTextures(1, &rt->canvas_group_levels[i].texture);
+ }
+ if (trunc < rt->canvas_group_levels.size()) {
+ rt->canvas_group_levels.resize(trunc);
+ }
+}
+
#endif // GLES3_ENABLED
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 8a03d72b9b93..8cd54605946d 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -347,6 +347,16 @@ struct RenderTarget {
GLuint backbuffer = 0;
GLuint backbuffer_depth = 0;
+ struct CanvasGroupLevel {
+ GLuint texture;
+ GLuint framebuffer;
+ int mipmap_count;
+ bool clear_needed = false;
+ bool mipmaps_generated = true;
+ uint64_t last_used = 0;
+ };
+ LocalVector canvas_group_levels;
+
bool hdr = false; // For Compatibility this effects both 2D and 3D rendering!
GLuint color_internal_format = GL_RGBA8;
GLuint color_format = GL_RGBA;
@@ -450,6 +460,7 @@ class TextureStorage : public RendererTextureStorage {
void _clear_render_target(RenderTarget *rt);
void _update_render_target(RenderTarget *rt);
void _create_render_target_backbuffer(RenderTarget *rt);
+ bool _create_render_target_canvas_group(RenderTarget *rt);
void _render_target_allocate_sdf(RenderTarget *rt);
void _render_target_clear_sdf(RenderTarget *rt);
Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const;
@@ -667,9 +678,15 @@ class TextureStorage : public RendererTextureStorage {
bool render_target_is_sdf_enabled(RID p_render_target) const;
void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
+ void render_target_copy_to_back_buffer_from_canvas_group(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps, uint32_t p_canvas_group_level);
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);
+ void render_target_clear_canvas_group(RID p_render_target, uint32_t p_canvas_group_level, bool p_allow_create = true);
+ void render_target_gen_canvas_group_mipmaps(RID p_render_target, const Rect2i &p_region, uint32_t p_canvas_group_level);
+ void render_target_set_canvas_group_needs_clear(RID p_render_target, uint32_t p_canvas_group_level);
+ void render_target_set_canvas_groups_used(RID p_render_target, uint32_t p_canvas_group_level);
+
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {}
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index c0386b056f76..aaeebda2e475 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -1278,7 +1278,7 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate");
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::INT, "clip_children", PROPERTY_HINT_ENUM, "Disabled,Clip Only,Clip + Draw"), "set_clip_children_mode", "get_clip_children_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_children", PROPERTY_HINT_ENUM, "Disabled:0,Clip Only:1,Clip + Draw:2,Subtract:4"), "set_clip_children_mode", "get_clip_children_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_layer", PROPERTY_HINT_LAYERS_2D_RENDER), "set_visibility_layer", "get_visibility_layer");
@@ -1327,6 +1327,7 @@ void CanvasItem::_bind_methods() {
BIND_ENUM_CONSTANT(CLIP_CHILDREN_DISABLED);
BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY);
BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW);
+ BIND_ENUM_CONSTANT(CLIP_CHILDREN_SUBTRACT);
BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX);
}
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 028c2cb2cfaa..80554d2d28d0 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -67,10 +67,11 @@ class CanvasItem : public Node {
};
enum ClipChildrenMode {
- CLIP_CHILDREN_DISABLED,
- CLIP_CHILDREN_ONLY,
- CLIP_CHILDREN_AND_DRAW,
- CLIP_CHILDREN_MAX,
+ CLIP_CHILDREN_DISABLED = 0,
+ CLIP_CHILDREN_ONLY = 1,
+ CLIP_CHILDREN_AND_DRAW = 2,
+ CLIP_CHILDREN_SUBTRACT = 4,
+ CLIP_CHILDREN_MAX = 5,
};
private:
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 018b7be81c00..a90cf395f99c 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -763,6 +763,11 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
code += "uniform sampler2D " + make_unique_id(p_type, p_id, "screen_tex") + " : hint_screen_texture;\n";
}
} break;
+ case SOURCE_MASK: {
+ if (p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mask_tex") + " : hint_mask_texture;\n";
+ }
+ } break;
case SOURCE_DEPTH:
case SOURCE_3D_NORMAL:
case SOURCE_ROUGHNESS: {
@@ -789,7 +794,7 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
- if (source == SOURCE_SCREEN) {
+ if (source == SOURCE_SCREEN || source == SOURCE_MASK) {
default_uv = "SCREEN_UV";
} else {
default_uv = "UV";
@@ -831,6 +836,17 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
return code;
}
} break;
+ case SOURCE_MASK: {
+ if (p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+ String id = make_unique_id(p_type, p_id, "mask_tex");
+ if (p_input_vars[1].is_empty()) {
+ code += " " + p_output_vars[0] + " = texture(" + id + ", " + uv + ");\n";
+ } else {
+ code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + uv + ", " + p_input_vars[1] + ");\n";
+ }
+ return code;
+ }
+ } break;
case SOURCE_2D_NORMAL:
case SOURCE_2D_TEXTURE: {
if (p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
@@ -908,6 +924,9 @@ void VisualShaderNodeTexture::set_source(Source p_source) {
case SOURCE_SCREEN:
simple_decl = false;
break;
+ case SOURCE_MASK:
+ simple_decl = false;
+ break;
case SOURCE_2D_TEXTURE:
simple_decl = false;
break;
@@ -984,6 +1003,11 @@ String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::T
return String(); // All good.
}
} break;
+ case SOURCE_MASK: {
+ if (p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
+ return String(); // All good.
+ }
+ } break;
case SOURCE_2D_NORMAL:
case SOURCE_2D_TEXTURE: {
if (p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) {
@@ -1017,7 +1041,7 @@ void VisualShaderNodeTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeTexture::set_texture_type);
ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTexture::get_texture_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D,Depth,SamplerPort,Normal3D,Roughness"), "set_source", "get_source");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture:0,Screen:1,Mask:8,Texture2D:2,NormalMap2D:3,Depth:4,SamplerPort:5,Normal3D:6,Roughness:7"), "set_source", "get_source");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type");
@@ -1029,6 +1053,7 @@ void VisualShaderNodeTexture::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_PORT);
BIND_ENUM_CONSTANT(SOURCE_3D_NORMAL);
BIND_ENUM_CONSTANT(SOURCE_ROUGHNESS);
+ BIND_ENUM_CONSTANT(SOURCE_MASK);
BIND_ENUM_CONSTANT(SOURCE_MAX);
BIND_ENUM_CONSTANT(TYPE_DATA);
@@ -6362,6 +6387,9 @@ String get_sampler_hint(VisualShaderNodeTextureParameter::TextureType p_texture_
case VisualShaderNodeTextureParameter::SOURCE_SCREEN:
source_code = "hint_screen_texture";
break;
+ case VisualShaderNodeTextureParameter::SOURCE_MASK:
+ source_code = "hint_mask_texture";
+ break;
case VisualShaderNodeTextureParameter::SOURCE_DEPTH:
source_code = "hint_depth_texture";
break;
@@ -6579,7 +6607,7 @@ void VisualShaderNodeTextureParameter::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White,Black,Transparent"), "set_color_default", "get_color_default");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Default,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Default,Enabled,Disabled"), "set_texture_repeat", "get_texture_repeat");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_source", PROPERTY_HINT_ENUM, "None,Screen,Depth,NormalRoughness"), "set_texture_source", "get_texture_source");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_source", PROPERTY_HINT_ENUM, "None:0,Screen:1,Mask:4,Depth:2,NormalRoughness:3"), "set_texture_source", "get_texture_source");
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
@@ -6610,6 +6638,7 @@ void VisualShaderNodeTextureParameter::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_SCREEN);
BIND_ENUM_CONSTANT(SOURCE_DEPTH);
BIND_ENUM_CONSTANT(SOURCE_NORMAL_ROUGHNESS);
+ BIND_ENUM_CONSTANT(SOURCE_MASK);
BIND_ENUM_CONSTANT(SOURCE_MAX);
}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 7a37ffa0e0d3..8a90bf2f6b8b 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -394,6 +394,7 @@ class VisualShaderNodeTexture : public VisualShaderNode {
SOURCE_PORT,
SOURCE_3D_NORMAL,
SOURCE_ROUGHNESS,
+ SOURCE_MASK,
SOURCE_MAX,
};
@@ -2518,6 +2519,7 @@ class VisualShaderNodeTextureParameter : public VisualShaderNodeParameter {
SOURCE_SCREEN,
SOURCE_DEPTH,
SOURCE_NORMAL_ROUGHNESS,
+ SOURCE_MASK,
SOURCE_MAX,
};
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index c4286dcc0c69..f80c73ad8db6 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -51,6 +51,7 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas
memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *));
for (int i = 0; i < p_child_item_count; i++) {
+ p_child_items[i].item->canvas_group_level = 0;
_cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, p_child_items[i].mirror, 1);
}
@@ -184,10 +185,6 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *
p_global_rect.position += p_clip_rect.position;
}
-
- // Very important that this is cleared after used in RendererCanvasRender to avoid
- // potential crashes.
- r_canvas_group_from->canvas_group_owner = ci;
}
}
@@ -357,6 +354,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
sorter.sort(child_items, child_item_count);
for (i = 0; i < child_item_count; i++) {
+ child_items[i]->canvas_group_level = ci->canvas_group_level;
_cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times);
}
} else {
@@ -377,10 +375,12 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
canvas_group_from = r_z_last_list[zidx];
}
+ uint32_t canvas_group_child_level = use_canvas_group ? ci->canvas_group_level + 1 : ci->canvas_group_level;
for (int i = 0; i < child_item_count; i++) {
if (!child_items[i]->behind && !use_canvas_group) {
continue;
}
+ child_items[i]->canvas_group_level = canvas_group_child_level;
_cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
}
_attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, final_xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from);
@@ -388,6 +388,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
if (child_items[i]->behind || use_canvas_group) {
continue;
}
+ child_items[i]->canvas_group_level = ci->canvas_group_level;
_cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
}
}
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index cb8180f9895e..2d849380de23 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -334,6 +334,7 @@ class RendererCanvasRender {
CanvasGroup *canvas_group = nullptr;
bool use_canvas_group = false;
+ uint32_t canvas_group_level = 0;
int light_mask;
int z_final;
@@ -357,7 +358,6 @@ class RendererCanvasRender {
Rect2 final_clip_rect;
Item *final_clip_owner = nullptr;
Item *material_owner = nullptr;
- Item *canvas_group_owner = nullptr;
ViewportRender *vp_render = nullptr;
bool distance_field;
bool light_masked;
@@ -467,7 +467,6 @@ class RendererCanvasRender {
vp_render = nullptr;
next = nullptr;
final_clip_owner = nullptr;
- canvas_group_owner = nullptr;
clip = false;
final_modulate = Color(1, 1, 1, 1);
visible = true;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index fa8cf9c0288f..236ba8526d7b 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -483,6 +483,32 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED;
+ if (p_item->use_canvas_group) {
+ switch (p_item->canvas_group->mode) {
+ case RS::CANVAS_GROUP_MODE_CLIP_ONLY: {
+ light_mode = PIPELINE_LIGHT_MODE_DISABLED_WITH_MASK;
+ base_flags |= 1 << FLAGS_MASK_MODE_SHIFT;
+
+ } break;
+ case RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW: {
+ light_mode = (light_mode == PIPELINE_LIGHT_MODE_ENABLED) ? PIPELINE_LIGHT_MODE_ENABLED_WITH_MASK : PIPELINE_LIGHT_MODE_DISABLED_WITH_MASK;
+ base_flags |= 2 << FLAGS_MASK_MODE_SHIFT;
+
+ } break;
+ case RS::CANVAS_GROUP_MODE_SUBTRACT: {
+ light_mode = (light_mode == PIPELINE_LIGHT_MODE_ENABLED) ? PIPELINE_LIGHT_MODE_ENABLED_WITH_MASK : PIPELINE_LIGHT_MODE_DISABLED_WITH_MASK;
+ base_flags |= 3 << FLAGS_MASK_MODE_SHIFT;
+
+ } break;
+ case RS::CANVAS_GROUP_MODE_TRANSPARENT: {
+ light_mode = PIPELINE_LIGHT_MODE_DISABLED_WITH_MASK;
+
+ } break;
+ case RS::CANVAS_GROUP_MODE_DISABLED: {
+ } break;
+ }
+ }
+
PipelineVariants *pipeline_variants = p_pipeline_variants;
bool reclip = false;
@@ -1055,7 +1081,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
}
}
-RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, bool p_backbuffer) {
+RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
@@ -1107,8 +1133,11 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 6;
RID screen;
- if (p_backbuffer) {
- screen = texture_storage->render_target_get_rd_texture(p_to_render_target);
+ if (p_alias_screen_to_mask) {
+ screen = texture_storage->render_target_get_rd_canvas_group(p_to_render_target, p_canvas_group_level);
+ if (screen.is_null()) { //treat missing mask as clear mask
+ screen = RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_TRANSPARENT);
+ }
} else {
screen = texture_storage->render_target_get_rd_backbuffer(p_to_render_target);
if (screen.is_null()) { //unallocated backbuffer
@@ -1123,6 +1152,19 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 7;
+ RID mask;
+ mask = texture_storage->render_target_get_rd_canvas_group(p_to_render_target, p_canvas_group_level);
+ if (mask.is_null()) { //treat missing mask as clear mask
+ mask = RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_TRANSPARENT);
+ }
+ u.append_id(mask);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 8;
RID sdf = texture_storage->render_target_get_sdf_texture(p_to_render_target);
u.append_id(sdf);
uniforms.push_back(u);
@@ -1139,16 +1181,16 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo
uniforms.append_array(material_storage->samplers_rd_get_default().get_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, BASE_UNIFORM_SET);
- if (p_backbuffer) {
- texture_storage->render_target_set_backbuffer_uniform_set(p_to_render_target, uniform_set);
+ if (p_canvas_group_level) {
+ texture_storage->render_target_set_canvas_group_uniform_set(p_to_render_target, uniform_set, p_alias_screen_to_mask);
} else {
- texture_storage->render_target_set_framebuffer_uniform_set(p_to_render_target, uniform_set);
+ texture_storage->render_target_set_framebuffer_uniform_set(p_to_render_target, uniform_set, p_alias_screen_to_mask);
}
return uniform_set;
}
-void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) {
+void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask, RenderingMethod::RenderInfo *r_render_info) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
@@ -1161,9 +1203,9 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
bool clear = false;
Vector clear_colors;
- if (p_to_backbuffer) {
- framebuffer = texture_storage->render_target_get_rd_backbuffer_framebuffer(p_to_render_target);
- fb_uniform_set = texture_storage->render_target_get_backbuffer_uniform_set(p_to_render_target);
+ if (p_canvas_group_level) {
+ framebuffer = texture_storage->render_target_get_rd_canvas_group_framebuffer(p_to_render_target, p_canvas_group_level - 1);
+ fb_uniform_set = texture_storage->render_target_get_canvas_group_uniform_set(p_to_render_target, p_alias_screen_to_mask);
} else {
framebuffer = texture_storage->render_target_get_rd_framebuffer(p_to_render_target);
texture_storage->render_target_set_msaa_needs_resolve(p_to_render_target, false); // If MSAA is enabled, our framebuffer will be resolved!
@@ -1174,11 +1216,11 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
texture_storage->render_target_disable_clear_request(p_to_render_target);
}
// TODO: Obtain from framebuffer format eventually when this is implemented.
- fb_uniform_set = texture_storage->render_target_get_framebuffer_uniform_set(p_to_render_target);
+ fb_uniform_set = texture_storage->render_target_get_framebuffer_uniform_set(p_to_render_target, p_alias_screen_to_mask);
}
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, p_to_backbuffer);
+ fb_uniform_set = _create_base_uniform_set(p_to_render_target, p_canvas_group_level, p_alias_screen_to_mask);
}
RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
@@ -1189,6 +1231,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET);
RID prev_material;
+ bool uses_mask_texture = false;
PipelineVariants *pipeline_variants = &shader.pipeline_variants;
@@ -1209,20 +1252,6 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
- if (ci->use_canvas_group) {
- if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- material = default_clip_children_material;
- } else {
- 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;
- }
- }
- }
- }
-
if (material != prev_material) {
CanvasMaterialData *material_data = nullptr;
if (material.is_valid()) {
@@ -1238,6 +1267,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET);
material_data->set_as_used();
}
+ uses_mask_texture = material_data->shader_data->uses_mask_texture || (p_alias_screen_to_mask && material_data->shader_data->uses_screen_texture);
} else {
pipeline_variants = &shader.pipeline_variants;
}
@@ -1246,6 +1276,11 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
}
}
+ if (uses_mask_texture) {
+ // If the shader uses the mask, disable the default handling
+ ci->use_canvas_group = false;
+ }
+
if (!ci->repeat_size.x && !ci->repeat_size.y) {
_render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info);
} else {
@@ -1495,23 +1530,27 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
Item *ci = p_item_list;
//fill the list until rendering is possible.
- bool material_screen_texture_cached = false;
+ uint32_t material_screen_texture_cached = -1;
bool material_screen_texture_mipmaps_cached = false;
Rect2 back_buffer_rect;
bool backbuffer_copy = false;
bool backbuffer_gen_mipmaps = false;
- Item *canvas_group_owner = nullptr;
+ const bool alias_screen_to_mask = true; // FIXME: Control this with a project setting?
+ uint32_t canvas_group_level = 0;
+ uint32_t canvas_group_max_level = 0;
bool skip_item = false;
bool update_skeletons = false;
bool time_used = false;
-
- bool backbuffer_cleared = false;
+ bool uses_mask_texture = false;
+ bool uses_mask_texture_mipmaps = false;
while (ci) {
- if (ci->copy_back_buffer && canvas_group_owner == nullptr) {
+ bool uses_mask_texture_explicit = false;
+
+ if (ci->copy_back_buffer) {
backbuffer_copy = true;
if (ci->copy_back_buffer->full) {
@@ -1526,8 +1565,8 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
if (material.is_valid()) {
CanvasMaterialData *md = static_cast(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D));
if (md && md->shader_data->valid) {
- if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) {
- if (!material_screen_texture_cached) {
+ if (md->shader_data->uses_screen_texture) {
+ if (material_screen_texture_cached != ci->canvas_group_level) {
backbuffer_copy = true;
back_buffer_rect = Rect2();
backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps;
@@ -1542,6 +1581,28 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
if (md->shader_data->uses_time) {
time_used = true;
}
+ if (ci->canvas_group != nullptr) {
+ if (md->shader_data->uses_mask_texture) {
+ uses_mask_texture_explicit = uses_mask_texture = true;
+ if (md->shader_data->uses_mask_texture_mipmaps) {
+ uses_mask_texture_mipmaps = true;
+ }
+ } else {
+ if (alias_screen_to_mask && md->shader_data->uses_screen_texture) {
+ uses_mask_texture = true;
+ }
+ if (md->shader_data->uses_mask_texture_mipmaps || (alias_screen_to_mask && md->shader_data->uses_screen_texture_mipmaps)) {
+ uses_mask_texture_mipmaps = true;
+ }
+ }
+ } else if (md->shader_data->uses_mask_texture) {
+ // An item that hasn't defined a canvas group receives an empty mask
+ texture_storage->render_target_clear_canvas_group(p_to_render_target, canvas_group_level, false);
+ if (md->shader_data->uses_mask_texture_mipmaps) {
+ // Clear the mipmaps too, if needed
+ texture_storage->render_target_gen_canvas_group_mipmaps(p_to_render_target, ci->global_rect_cache, canvas_group_level);
+ }
+ }
}
}
@@ -1561,61 +1622,75 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
}
}
- if (ci->canvas_group_owner != nullptr) {
- if (canvas_group_owner == nullptr) {
- // Canvas group begins here, render until before this item
- if (update_skeletons) {
- mesh_storage->update_mesh_instances();
- update_skeletons = false;
- }
-
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
- item_count = 0;
+ if (ci->canvas_group_level > canvas_group_level) {
+ // Canvas group begins here, render until before this item
+ if (update_skeletons) {
+ mesh_storage->update_mesh_instances();
+ update_skeletons = false;
+ }
- if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) {
- Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
- texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
- if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- ci->canvas_group_owner->use_canvas_group = false;
- items[item_count++] = ci->canvas_group_owner;
- }
- } else if (!backbuffer_cleared) {
- texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
- backbuffer_cleared = true;
- }
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, false, r_render_info);
+ item_count = 0;
- backbuffer_copy = false;
- canvas_group_owner = ci->canvas_group_owner; //continue until owner found
+ while (ci->canvas_group_level > canvas_group_level) {
+ texture_storage->render_target_clear_canvas_group(p_to_render_target, canvas_group_level);
+ canvas_group_level++;
}
+ texture_storage->render_target_clear_canvas_group_uniform_set(p_to_render_target);
- ci->canvas_group_owner = nullptr; //must be cleared
- }
-
- if (canvas_group_owner == nullptr && ci->canvas_group != nullptr && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- skip_item = true;
+ if (canvas_group_max_level < canvas_group_level) {
+ canvas_group_max_level = canvas_group_level;
+ }
}
- if (ci == canvas_group_owner) {
+ if (ci->canvas_group_level < canvas_group_level) {
if (update_skeletons) {
mesh_storage->update_mesh_instances();
update_skeletons = false;
}
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, false, r_render_info);
item_count = 0;
- if (ci->canvas_group->blur_mipmaps) {
- texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, ci->global_rect_cache);
+ if (material_screen_texture_cached > ci->canvas_group_level) {
+ // A backbuffer from a canvas group that ended is no longer valid
+ material_screen_texture_cached = -1;
+ }
+
+ // Every reduction in canvas group level must be accompanied by a canvas group parent being drawn
+ DEV_ASSERT(ci->canvas_group != nullptr);
+ DEV_ASSERT(canvas_group_level - 1 == ci->canvas_group_level);
+ canvas_group_level = ci->canvas_group_level;
+
+ if (ci->canvas_group->blur_mipmaps || uses_mask_texture_mipmaps) {
+ texture_storage->render_target_gen_canvas_group_mipmaps(p_to_render_target, ci->global_rect_cache, canvas_group_level);
}
+ texture_storage->render_target_clear_canvas_group_uniform_set(p_to_render_target);
- canvas_group_owner = nullptr;
- // Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it.
- backbuffer_cleared = false;
+ // The mask buffer is dirty now and needs to be cleared if another element tries to use it
+ texture_storage->render_target_set_canvas_group_needs_clear(p_to_render_target, canvas_group_level);
+ uses_mask_texture = false;
+ uses_mask_texture_mipmaps = false;
// Tell the renderer to paint this as a canvas group
ci->use_canvas_group = true;
} else {
ci->use_canvas_group = false;
+ if (ci->canvas_group != nullptr) {
+ if (uses_mask_texture) {
+ // A canvas group with no children, and a custom shader. Prevent it from receiving a dirty mask texture
+ texture_storage->render_target_clear_canvas_group(p_to_render_target, canvas_group_level, false);
+ if (uses_mask_texture_mipmaps) {
+ // Clear the mipmaps too, if needed
+ texture_storage->render_target_gen_canvas_group_mipmaps(p_to_render_target, ci->global_rect_cache, canvas_group_level);
+ }
+ uses_mask_texture = false;
+ uses_mask_texture_mipmaps = false;
+ } else if (ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_SUBTRACT) {
+ // Skip CanvasGroup or clip-only parent with no children if it isn't using a custom blend for the mask
+ skip_item = true;
+ }
+ }
}
if (backbuffer_copy) {
@@ -1625,13 +1700,17 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
update_skeletons = false;
}
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, false, r_render_info);
item_count = 0;
- texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
+ if (canvas_group_level > 0) {
+ texture_storage->render_target_copy_to_back_buffer_from_canvas_group(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps, canvas_group_level - 1);
+ } else {
+ texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
+ }
backbuffer_copy = false;
- material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies.
+ material_screen_texture_cached = ci->canvas_group_level; // After a backbuffer copy, screen texture makes no further copies.
material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps;
backbuffer_gen_mipmaps = false;
}
@@ -1649,13 +1728,13 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
items[item_count++] = ci;
}
- if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
+ if (!ci->next || item_count == MAX_RENDER_ITEMS - 1 || (alias_screen_to_mask && ci->use_canvas_group && !uses_mask_texture_explicit)) {
if (update_skeletons) {
mesh_storage->update_mesh_instances();
update_skeletons = false;
}
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_level, alias_screen_to_mask && ci->use_canvas_group && !uses_mask_texture_explicit, r_render_info);
//then reset
item_count = 0;
}
@@ -1663,6 +1742,8 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
ci = ci->next;
}
+ texture_storage->render_target_set_canvas_groups_used(p_to_render_target, canvas_group_max_level);
+
if (time_used) {
RenderingServerDefault::redraw_request();
}
@@ -2169,6 +2250,8 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
uniforms.clear();
uses_screen_texture = false;
uses_screen_texture_mipmaps = false;
+ uses_mask_texture = false;
+ uses_mask_texture_mipmaps = false;
uses_sdf = false;
uses_time = false;
@@ -2204,6 +2287,8 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
uses_screen_texture = gen_code.uses_screen_texture;
+ uses_mask_texture_mipmaps = gen_code.uses_mask_texture_mipmaps;
+ uses_mask_texture = gen_code.uses_mask_texture;
if (version.is_null()) {
version = canvas_singleton->shader.canvas_shader.version_create();
@@ -2357,6 +2442,34 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
SHADER_VARIANT_QUAD_LIGHT,
},
+ {
+ //non lit masked
+ SHADER_VARIANT_QUAD_MASK,
+ SHADER_VARIANT_NINEPATCH_MASK,
+ SHADER_VARIANT_PRIMITIVE_MASK,
+ SHADER_VARIANT_PRIMITIVE_MASK,
+ SHADER_VARIANT_PRIMITIVE_POINTS_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_POINTS_MASK,
+ SHADER_VARIANT_QUAD_MASK,
+ },
+ {
+ //lit masked
+ SHADER_VARIANT_QUAD_LIGHT_MASK,
+ SHADER_VARIANT_NINEPATCH_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT_MASK,
+ SHADER_VARIANT_QUAD_LIGHT_MASK,
+ },
};
RID shader_variant = canvas_singleton->shader.canvas_shader.version_get_shader(version, shader_variants[i][j]);
@@ -2466,6 +2579,20 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size
variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n"); // attributes for vertex arrays
variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size
+ //non light masked variants
+ variants.push_back("#define USE_MASK\n"); //none by default is first variant
+ variants.push_back("#define USE_MASK\n#define USE_NINEPATCH\n"); //ninepatch is the second variant
+ variants.push_back("#define USE_MASK\n#define USE_PRIMITIVE\n"); //primitive is the third
+ variants.push_back("#define USE_MASK\n#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size
+ variants.push_back("#define USE_MASK\n#define USE_ATTRIBUTES\n"); // attributes for vertex arrays
+ variants.push_back("#define USE_MASK\n#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size
+ //light masked variants
+ variants.push_back("#define USE_MASK\n#define USE_LIGHTING\n"); //none by default is first variant
+ variants.push_back("#define USE_MASK\n#define USE_LIGHTING\n#define USE_NINEPATCH\n"); //ninepatch is the second variant
+ variants.push_back("#define USE_MASK\n#define USE_LIGHTING\n#define USE_PRIMITIVE\n"); //primitive is the third
+ variants.push_back("#define USE_MASK\n#define USE_LIGHTING\n#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size
+ variants.push_back("#define USE_MASK\n#define USE_LIGHTING\n#define USE_ATTRIBUTES\n"); // attributes for vertex arrays
+ variants.push_back("#define USE_MASK\n#define USE_LIGHTING\n#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size
shader.canvas_shader.initialize(variants, global_defines);
@@ -2543,6 +2670,34 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
SHADER_VARIANT_QUAD_LIGHT,
},
+ {
+ //non lit masked
+ SHADER_VARIANT_QUAD_MASK,
+ SHADER_VARIANT_NINEPATCH_MASK,
+ SHADER_VARIANT_PRIMITIVE_MASK,
+ SHADER_VARIANT_PRIMITIVE_MASK,
+ SHADER_VARIANT_PRIMITIVE_POINTS_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_POINTS_MASK,
+ SHADER_VARIANT_QUAD_MASK,
+ },
+ {
+ //lit masked
+ SHADER_VARIANT_QUAD_LIGHT_MASK,
+ SHADER_VARIANT_NINEPATCH_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT_MASK,
+ SHADER_VARIANT_QUAD_LIGHT_MASK,
+ },
};
RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[i][j]);
@@ -2787,58 +2942,9 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
state.time = 0;
- {
- default_canvas_group_shader = material_storage->shader_allocate();
- material_storage->shader_initialize(default_canvas_group_shader);
-
- material_storage->shader_set_code(default_canvas_group_shader, R"(
-// Default CanvasGroup shader.
-
-shader_type canvas_item;
-render_mode unshaded;
-
-uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
-
-void fragment() {
- vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0);
-
- if (c.a > 0.0001) {
- c.rgb /= c.a;
- }
-
- COLOR *= c;
-}
-)");
- default_canvas_group_material = material_storage->material_allocate();
- material_storage->material_initialize(default_canvas_group_material);
-
- material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader);
- }
-
- {
- default_clip_children_shader = material_storage->shader_allocate();
- material_storage->shader_initialize(default_clip_children_shader);
-
- material_storage->shader_set_code(default_clip_children_shader, R"(
-// Default clip children shader.
-
-shader_type canvas_item;
-render_mode unshaded;
-
-uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
-
-void fragment() {
- vec4 c = textureLod(screen_texture, SCREEN_UV, 0.0);
- COLOR.rgb = c.rgb;
-}
-)");
- default_clip_children_material = material_storage->material_allocate();
- material_storage->material_initialize(default_clip_children_material);
-
- material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader);
- }
-
- static_assert(sizeof(PushConstant) == 128);
+ // This is the minimum push constant size the Vulkan spec guarantees
+ // Any larger, and some devices might not be supported
+ static_assert(sizeof(PushConstant) <= 128);
}
bool RendererCanvasRenderRD::free(RID p_rid) {
@@ -2889,15 +2995,8 @@ void RendererCanvasRenderRD::set_debug_redraw(bool p_enabled, double p_time, con
}
RendererCanvasRenderRD::~RendererCanvasRenderRD() {
- RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
//canvas state
- material_storage->material_free(default_canvas_group_material);
- material_storage->shader_free(default_canvas_group_shader);
-
- material_storage->material_free(default_clip_children_material);
- material_storage->shader_free(default_clip_children_shader);
-
{
if (state.canvas_state_buffer.is_valid()) {
RD::get_singleton()->free(state.canvas_state_buffer);
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index a78ced271a03..039d94c3d0b5 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -63,6 +63,18 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT,
SHADER_VARIANT_ATTRIBUTES_LIGHT,
SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
+ SHADER_VARIANT_QUAD_MASK,
+ SHADER_VARIANT_NINEPATCH_MASK,
+ SHADER_VARIANT_PRIMITIVE_MASK,
+ SHADER_VARIANT_PRIMITIVE_POINTS_MASK,
+ SHADER_VARIANT_ATTRIBUTES_MASK,
+ SHADER_VARIANT_ATTRIBUTES_POINTS_MASK,
+ SHADER_VARIANT_QUAD_LIGHT_MASK,
+ SHADER_VARIANT_NINEPATCH_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_LIGHT_MASK,
+ SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_LIGHT_MASK,
+ SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT_MASK,
SHADER_VARIANT_MAX
};
@@ -83,6 +95,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
FLAGS_LIGHT_COUNT_SHIFT = 20,
+ FLAGS_MASK_MODE_SHIFT = 24,
FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27),
@@ -135,6 +148,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
enum PipelineLightMode {
PIPELINE_LIGHT_MODE_DISABLED,
PIPELINE_LIGHT_MODE_ENABLED,
+ PIPELINE_LIGHT_MODE_DISABLED_WITH_MASK,
+ PIPELINE_LIGHT_MODE_ENABLED_WITH_MASK,
PIPELINE_LIGHT_MODE_MAX
};
@@ -176,6 +191,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
bool uses_screen_texture = false;
bool uses_screen_texture_mipmaps = false;
+ bool uses_mask_texture = false;
+ bool uses_mask_texture_mipmaps = false;
bool uses_sdf = false;
bool uses_time = false;
@@ -408,15 +425,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
bool using_directional_lights = false;
RID default_canvas_texture;
- RID default_canvas_group_shader;
- RID default_canvas_group_material;
- RID default_clip_children_material;
- RID default_clip_children_shader;
-
RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
- RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer);
+ RID _create_base_uniform_set(RID p_to_render_target, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask);
bool debug_redraw = false;
Color debug_redraw_color;
@@ -424,7 +436,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
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, bool p_texture_is_data = false); //recursive, so regular inline used instead.
void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info = nullptr);
- void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
+ void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, uint32_t p_canvas_group_level, bool p_alias_screen_to_mask = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
_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);
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 4426d9eb6622..3474fb286e4b 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -564,7 +564,7 @@ void main() {
specular_shininess = vec4(1.0);
}
-#if defined(SCREEN_UV_USED)
+#if defined(SCREEN_UV_USED) || defined(USE_MASK)
vec2 screen_uv = gl_FragCoord.xy * canvas_data.screen_pixel_size;
#else
vec2 screen_uv = vec2(0.0);
@@ -740,5 +740,30 @@ void main() {
color.a *= light_only_alpha;
#endif
+#if defined(USE_MASK)
+ {
+ uint mask_mode = (draw_data.flags >> FLAGS_MASK_MODE_SHIFT) & 3;
+ vec4 mask = textureLod(sampler2D(mask_buffer, SAMPLER_LINEAR_CLAMP), screen_uv, 0.0);
+
+ if (mask_mode == 0) {
+ if (mask.a > 0.0001) {
+ mask.rgb /= mask.a;
+ }
+ color *= mask;
+ } else if (mask_mode == 1) {
+ if (mask.a > 0.0001) {
+ mask.rgb /= mask.a;
+ }
+ mask.a *= color.a;
+ color = mask;
+ } else if (mask_mode == 2) {
+ color.rgb *= 1.0 - mask.a;
+ color.rgb += mask.rgb;
+ } else if (mask_mode == 3) {
+ color.a *= 1.0 - mask.a;
+ }
+ }
+#endif
+
frag_color = color;
}
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index 8649f4710bf3..477a9aa707da 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -19,6 +19,7 @@
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
#define FLAGS_LIGHT_COUNT_SHIFT 20
+#define FLAGS_MASK_MODE_SHIFT 24
#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26)
#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27)
@@ -124,7 +125,8 @@ layout(set = 0, binding = 4) uniform texture2D shadow_atlas_texture;
layout(set = 0, binding = 5) uniform sampler shadow_sampler;
layout(set = 0, binding = 6) uniform texture2D color_buffer;
-layout(set = 0, binding = 7) uniform texture2D sdf_texture;
+layout(set = 0, binding = 7) uniform texture2D mask_buffer;
+layout(set = 0, binding = 8) uniform texture2D sdf_texture;
#include "samplers_inc.glsl"
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 13b00be1c434..623dbd181d30 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -654,6 +654,7 @@ void MaterialStorage::MaterialData::update_uniform_buffer(const HashMap textures;
if (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
+ p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_MASK_TEXTURE ||
p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
continue;
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 6e5e8f63e0e6..48f6a4c7ff6b 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -3025,6 +3025,9 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
if (rt->framebuffer_uniform_set.is_valid()) {
rt->framebuffer_uniform_set = RID(); //chain deleted
}
+ if (rt->framebuffer_uniform_set_alias_screen_to_mask.is_valid()) {
+ rt->framebuffer_uniform_set_alias_screen_to_mask = RID(); //chain deleted
+ }
if (rt->color.is_valid()) {
RD::get_singleton()->free(rt->color);
@@ -3039,9 +3042,22 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
RD::get_singleton()->free(rt->backbuffer);
rt->backbuffer = RID();
rt->backbuffer_mipmaps.clear();
- rt->backbuffer_uniform_set = RID(); //chain deleted
}
+ if (rt->canvas_group_uniform_set.is_valid()) {
+ rt->canvas_group_uniform_set = RID(); // freed as dependent on the SDF texture
+ }
+ if (rt->canvas_group_uniform_set_alias_screen_to_mask.is_valid()) {
+ rt->canvas_group_uniform_set_alias_screen_to_mask = RID(); // freed as dependent on the SDF texture
+ }
+ for (uint32_t i = 0; i < rt->canvas_group_levels.size(); i++) {
+ RD::get_singleton()->free(rt->canvas_group_levels[i].texture); // this also frees the framebuffer and mipmaps
+ if (rt->canvas_group_levels[i].multisample.is_valid()) {
+ RD::get_singleton()->free(rt->canvas_group_levels[i].multisample);
+ }
+ }
+ rt->canvas_group_levels.clear();
+
_render_target_clear_sdf(rt);
rt->color = RID();
@@ -3195,6 +3211,10 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) {
RD::get_singleton()->free(rt->framebuffer_uniform_set);
rt->framebuffer_uniform_set = RID();
}
+ if (rt->canvas_group_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->canvas_group_uniform_set)) {
+ RD::get_singleton()->free(rt->canvas_group_uniform_set);
+ rt->canvas_group_uniform_set = RID();
+ }
//create mipmaps
for (uint32_t i = 1; i < mipmaps_required; i++) {
RID mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i);
@@ -3204,6 +3224,75 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) {
}
}
+void TextureStorage::_create_render_target_canvas_group(RenderTarget *rt) {
+ int index = rt->canvas_group_levels.size();
+ RenderTarget::CanvasGroupLevel canvas_group_level;
+
+ uint32_t mipmaps_required = Image::get_image_required_mipmaps(rt->size.width, rt->size.height, Image::FORMAT_RGBA8);
+ RD::TextureFormat tf;
+ tf.format = rt->color_format;
+ tf.width = rt->size.width;
+ tf.height = rt->size.height;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ 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;
+ if (rt->msaa != RS::VIEWPORT_MSAA_DISABLED) {
+ tf.is_resolve_buffer = true;
+ }
+
+ canvas_group_level.texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(canvas_group_level.texture, "Render Target Canvas Group Level " + itos(index));
+
+ if (rt->msaa != RS::VIEWPORT_MSAA_DISABLED) {
+ tf.is_resolve_buffer = false;
+ const RD::TextureSamples texture_samples[RS::VIEWPORT_MSAA_MAX] = {
+ RD::TEXTURE_SAMPLES_1,
+ RD::TEXTURE_SAMPLES_2,
+ RD::TEXTURE_SAMPLES_4,
+ RD::TEXTURE_SAMPLES_8,
+ };
+ tf.mipmaps = 1;
+ tf.samples = texture_samples[rt->msaa];
+ tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ tf.is_resolve_buffer = false;
+ canvas_group_level.multisample = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(canvas_group_level.multisample, "Render Target Canvas Group Level " + itos(index) + " Multisample");
+ }
+
+ canvas_group_level.mipmap0 = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), canvas_group_level.texture, 0, 0);
+ RD::get_singleton()->set_resource_name(canvas_group_level.mipmap0, "Canvas Group Level " + itos(index) + " slice mipmap 0");
+
+ {
+ Vector fb_tex;
+ if (rt->msaa != RS::VIEWPORT_MSAA_DISABLED) {
+ fb_tex.push_back(canvas_group_level.multisample);
+ }
+ fb_tex.push_back(canvas_group_level.mipmap0);
+ canvas_group_level.framebuffer = RD::get_singleton()->framebuffer_create(fb_tex);
+ }
+
+ if (index == 0) {
+ //the cached uniform set may be using a blank texture which must be replaced with this one
+ if (rt->framebuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->framebuffer_uniform_set)) {
+ RD::get_singleton()->free(rt->framebuffer_uniform_set);
+ rt->framebuffer_uniform_set = RID();
+ }
+ if (rt->framebuffer_uniform_set_alias_screen_to_mask.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->framebuffer_uniform_set_alias_screen_to_mask)) {
+ RD::get_singleton()->free(rt->framebuffer_uniform_set_alias_screen_to_mask);
+ rt->framebuffer_uniform_set_alias_screen_to_mask = RID();
+ }
+ }
+ //create mipmaps
+ for (uint32_t i = 1; i < mipmaps_required; i++) {
+ RID mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), canvas_group_level.texture, 0, i);
+ RD::get_singleton()->set_resource_name(mipmap, "Canvas Group Level " + itos(index) + " slice mip: " + itos(i));
+
+ canvas_group_level.mipmaps.push_back(mipmap);
+ }
+
+ rt->canvas_group_levels.push_back(canvas_group_level);
+}
+
RID TextureStorage::render_target_create() {
RenderTarget render_target;
@@ -3474,15 +3563,27 @@ RID TextureStorage::render_target_get_rd_backbuffer(RID p_render_target) {
return rt->backbuffer;
}
-RID TextureStorage::render_target_get_rd_backbuffer_framebuffer(RID p_render_target) {
+RID TextureStorage::render_target_get_rd_canvas_group(RID p_render_target, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL_V(rt, RID());
+ if (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ // This is not an error condition.
+ // This indicates to the caller that a canvas group level has not been allocated,
+ // and a default transparent texture should be used.
+ return RID();
+ }
+ return rt->canvas_group_levels[p_canvas_group_level].texture;
+}
+
+RID TextureStorage::render_target_get_rd_canvas_group_framebuffer(RID p_render_target, uint32_t p_canvas_group_level) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL_V(rt, RID());
- if (!rt->backbuffer.is_valid()) {
- _create_render_target_backbuffer(rt);
+ while (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ _create_render_target_canvas_group(rt);
}
- return rt->backbuffer_fb;
+ return rt->canvas_group_levels[p_canvas_group_level].framebuffer;
}
void TextureStorage::render_target_request_clear(RID p_render_target, const Color &p_clear_color) {
@@ -3869,6 +3970,62 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
RD::get_singleton()->draw_command_end_label();
}
+void TextureStorage::render_target_copy_to_back_buffer_from_canvas_group(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps, uint32_t p_canvas_group_level) {
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
+
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+ if (!rt->backbuffer.is_valid()) {
+ _create_render_target_backbuffer(rt);
+ }
+
+ ERR_FAIL_COND(p_canvas_group_level >= rt->canvas_group_levels.size());
+ const RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ Rect2i region;
+ if (p_region == Rect2i()) {
+ region.size = rt->size;
+ } else {
+ region = Rect2i(Size2i(), rt->size).intersection(p_region);
+ if (region.size == Size2i()) {
+ return; //nothing to do
+ }
+ }
+
+ if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
+ copy_effects->copy_to_rect(canvas_group_level.mipmap0, rt->backbuffer_mipmap0, region, false, false, false, true, true);
+ } else {
+ copy_effects->copy_to_fb_rect(canvas_group_level.mipmap0, rt->backbuffer_fb, region, false, false, false, false, RID(), false, true);
+ }
+
+ if (!p_gen_mipmaps) {
+ return;
+ }
+ RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps");
+ //then mipmap blur
+ RID prev_texture = canvas_group_level.mipmap0;
+ Size2i texture_size = rt->size;
+
+ 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);
+ 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, texture_size, true);
+ } else {
+ copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
+ }
+ prev_texture = mipmap;
+ }
+ RD::get_singleton()->draw_command_end_label();
+}
+
void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
@@ -3941,27 +4098,178 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
RD::get_singleton()->draw_command_end_label();
}
-RID TextureStorage::render_target_get_framebuffer_uniform_set(RID p_render_target) {
+void TextureStorage::render_target_clear_canvas_group(RID p_render_target, uint32_t p_canvas_group_level, bool p_allow_create) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ if (!p_allow_create && p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ // This is not an error state.
+ // This simply indicates that a blank transparent texture should be used instead.
+ return;
+ }
+
+ while (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ _create_render_target_canvas_group(rt);
+ }
+ RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ if (!canvas_group_level.clear_needed) {
+ // Previously cleared, and no new items drawn
+ return;
+ }
+ canvas_group_level.clear_needed = false;
+ canvas_group_level.mipmaps_generated = false;
+
+ Vector clear_colors;
+ clear_colors.push_back(Color(0, 0, 0, 0));
+ RD::get_singleton()->draw_list_begin(canvas_group_level.framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors);
+ RD::get_singleton()->draw_list_end();
+}
+
+void TextureStorage::render_target_gen_canvas_group_mipmaps(RID p_render_target, const Rect2i &p_region, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
+
+ if (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ // Let the caller use the default transparent. It doesn't need mipmaps
+ return;
+ }
+ RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ if (canvas_group_level.mipmaps_generated) {
+ // Cached mipmaps exist
+ return;
+ }
+ canvas_group_level.mipmaps_generated = true;
+
+ Rect2i region;
+ if (p_region == Rect2i()) {
+ region.size = rt->size;
+ } else {
+ region = Rect2i(Size2i(), rt->size).intersection(p_region);
+ if (region.size == Size2i()) {
+ return; //nothing to do
+ }
+ }
+ RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps2");
+ //then mipmap blur
+ RID prev_texture = canvas_group_level.mipmap0;
+ Size2i texture_size = rt->size;
+
+ for (int i = 0; i < canvas_group_level.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);
+ texture_size.x = MAX(1, texture_size.x >> 1);
+ texture_size.y = MAX(1, texture_size.y >> 1);
+
+ RID mipmap = canvas_group_level.mipmaps[i];
+
+ if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) {
+ copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true);
+ } else {
+ copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size);
+ }
+ prev_texture = mipmap;
+ }
+ RD::get_singleton()->draw_command_end_label();
+}
+
+void TextureStorage::render_target_set_canvas_group_needs_clear(RID p_render_target, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ if (p_canvas_group_level >= rt->canvas_group_levels.size()) {
+ // Transparent default doesn't need to be any more clear
+ return;
+ }
+ RenderTarget::CanvasGroupLevel &canvas_group_level = rt->canvas_group_levels[p_canvas_group_level];
+
+ canvas_group_level.clear_needed = true;
+}
+
+void TextureStorage::render_target_set_canvas_groups_used(RID p_render_target, uint32_t p_canvas_group_level) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+
+ uint64_t last_used = OS::get_singleton()->get_ticks_usec();
+
+ uint32_t i;
+ for (i = 0; i < p_canvas_group_level; i++) {
+ rt->canvas_group_levels[i].last_used = last_used;
+ }
+ for (; i < rt->canvas_group_levels.size(); i++) {
+ if (last_used - rt->canvas_group_levels[i].last_used > 2000000L) {
+ // Release levels that haven't been used for more than 2 seconds
+ break;
+ }
+ }
+ uint32_t trunc = i;
+ for (; i < rt->canvas_group_levels.size(); i++) {
+ RD::get_singleton()->free(rt->canvas_group_levels[i].texture);
+ if (rt->canvas_group_levels[i].multisample.is_valid()) {
+ RD::get_singleton()->free(rt->canvas_group_levels[i].multisample);
+ }
+ }
+ if (trunc < rt->canvas_group_levels.size()) {
+ rt->canvas_group_levels.resize(trunc);
+ }
+}
+
+RID TextureStorage::render_target_get_framebuffer_uniform_set(RID p_render_target, bool p_alias_screen_to_mask) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL_V(rt, RID());
- return rt->framebuffer_uniform_set;
+ if (p_alias_screen_to_mask) {
+ return rt->framebuffer_uniform_set_alias_screen_to_mask;
+ } else {
+ return rt->framebuffer_uniform_set;
+ }
}
-RID TextureStorage::render_target_get_backbuffer_uniform_set(RID p_render_target) {
+RID TextureStorage::render_target_get_canvas_group_uniform_set(RID p_render_target, bool p_alias_screen_to_mask) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL_V(rt, RID());
- return rt->backbuffer_uniform_set;
+ if (p_alias_screen_to_mask) {
+ return rt->canvas_group_uniform_set_alias_screen_to_mask;
+ } else {
+ return rt->canvas_group_uniform_set;
+ }
}
-void TextureStorage::render_target_set_framebuffer_uniform_set(RID p_render_target, RID p_uniform_set) {
+void TextureStorage::render_target_set_framebuffer_uniform_set(RID p_render_target, RID p_uniform_set, bool p_alias_screen_to_mask) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
- rt->framebuffer_uniform_set = p_uniform_set;
+ if (p_alias_screen_to_mask) {
+ rt->framebuffer_uniform_set_alias_screen_to_mask = p_uniform_set;
+ } else {
+ rt->framebuffer_uniform_set = p_uniform_set;
+ }
}
-void TextureStorage::render_target_set_backbuffer_uniform_set(RID p_render_target, RID p_uniform_set) {
+void TextureStorage::render_target_set_canvas_group_uniform_set(RID p_render_target, RID p_uniform_set, bool p_alias_screen_to_mask) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
- rt->backbuffer_uniform_set = p_uniform_set;
+ if (p_alias_screen_to_mask) {
+ rt->canvas_group_uniform_set_alias_screen_to_mask = p_uniform_set;
+ } else {
+ rt->canvas_group_uniform_set = p_uniform_set;
+ }
+}
+
+void TextureStorage::render_target_clear_canvas_group_uniform_set(RID p_render_target) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_NULL(rt);
+ if (rt->canvas_group_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->canvas_group_uniform_set)) {
+ RD::get_singleton()->free(rt->canvas_group_uniform_set);
+ rt->canvas_group_uniform_set = RID();
+ }
+ if (rt->canvas_group_uniform_set_alias_screen_to_mask.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->canvas_group_uniform_set_alias_screen_to_mask)) {
+ RD::get_singleton()->free(rt->canvas_group_uniform_set_alias_screen_to_mask);
+ rt->canvas_group_uniform_set_alias_screen_to_mask = RID();
+ }
}
void TextureStorage::render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) {
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 704f5fb1bdad..6414bf7e77a1 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -352,8 +352,22 @@ class TextureStorage : public RendererTextureStorage {
Vector backbuffer_mipmaps;
+ struct CanvasGroupLevel {
+ RID texture;
+ RID multisample;
+ RID framebuffer;
+ RID mipmap0;
+ Vector mipmaps;
+ bool clear_needed = false;
+ bool mipmaps_generated = true;
+ uint64_t last_used = 0;
+ };
+ LocalVector canvas_group_levels;
+
RID framebuffer_uniform_set;
- RID backbuffer_uniform_set;
+ RID framebuffer_uniform_set_alias_screen_to_mask;
+ RID canvas_group_uniform_set;
+ RID canvas_group_uniform_set_alias_screen_to_mask;
RID sdf_buffer_write;
RID sdf_buffer_write_fb;
@@ -423,6 +437,7 @@ class TextureStorage : public RendererTextureStorage {
void _clear_render_target(RenderTarget *rt);
void _update_render_target(RenderTarget *rt);
void _create_render_target_backbuffer(RenderTarget *rt);
+ void _create_render_target_canvas_group(RenderTarget *rt);
void _render_target_allocate_sdf(RenderTarget *rt);
void _render_target_clear_sdf(RenderTarget *rt);
Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const;
@@ -727,9 +742,14 @@ class TextureStorage : public RendererTextureStorage {
virtual bool render_target_is_using_hdr(RID p_render_target) const override;
void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
+ void render_target_copy_to_back_buffer_from_canvas_group(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps, uint32_t p_canvas_group_level);
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);
+
+ void render_target_clear_canvas_group(RID p_render_target, uint32_t p_canvas_group_level, bool p_allow_create = true);
+ void render_target_gen_canvas_group_mipmaps(RID p_render_target, const Rect2i &p_region, uint32_t p_canvas_group_level);
+ void render_target_set_canvas_group_needs_clear(RID p_render_target, uint32_t p_canvas_group_level);
+ void render_target_set_canvas_groups_used(RID p_render_target, uint32_t p_canvas_group_level);
virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override;
virtual bool render_target_is_clear_requested(RID p_render_target) override;
@@ -766,13 +786,15 @@ class TextureStorage : public RendererTextureStorage {
RID render_target_get_rd_texture_slice(RID p_render_target, uint32_t p_layer);
RID render_target_get_rd_texture_msaa(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_rd_canvas_group(RID p_render_target, uint32_t p_canvas_group_level);
+ RID render_target_get_rd_canvas_group_framebuffer(RID p_render_target, uint32_t p_canvas_group_level);
- 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_framebuffer_uniform_set(RID p_render_target, bool p_alias_screen_to_mask);
+ RID render_target_get_canvas_group_uniform_set(RID p_render_target, bool p_alias_screen_to_mask);
- void render_target_set_framebuffer_uniform_set(RID p_render_target, RID p_uniform_set);
- void render_target_set_backbuffer_uniform_set(RID p_render_target, RID p_uniform_set);
+ void render_target_set_framebuffer_uniform_set(RID p_render_target, RID p_uniform_set, bool p_alias_screen_to_mask);
+ void render_target_set_canvas_group_uniform_set(RID p_render_target, RID p_uniform_set, bool p_alias_screen_to_mask);
+ void render_target_clear_canvas_group_uniform_set(RID p_render_target);
};
} // namespace RendererRD
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index a4ee33ecc0a2..44f6d8ff6de6 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -509,6 +509,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
for (const KeyValue &E : pnode->uniforms) {
if (SL::is_sampler_type(E.value.type)) {
if (E.value.hint == SL::ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
+ E.value.hint == SL::ShaderNode::Uniform::HINT_MASK_TEXTURE ||
E.value.hint == SL::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
E.value.hint == SL::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
continue; // Don't create uniforms in the generated code for these.
@@ -554,6 +555,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
if (uniform.hint == SL::ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
+ uniform.hint == SL::ShaderNode::Uniform::HINT_MASK_TEXTURE ||
uniform.hint == SL::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
uniform.hint == SL::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
continue; // Don't create uniforms in the generated code for these.
@@ -930,6 +932,12 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
r_gen_code.uses_screen_texture_mipmaps = true;
}
r_gen_code.uses_screen_texture = true;
+ } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_MASK_TEXTURE) {
+ name = "mask_buffer";
+ if (u.filter >= ShaderLanguage::FILTER_NEAREST_MIPMAP) {
+ r_gen_code.uses_mask_texture_mipmaps = true;
+ }
+ r_gen_code.uses_mask_texture = true;
} else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) {
name = "normal_roughness_buffer";
r_gen_code.uses_normal_roughness_texture = true;
@@ -1160,6 +1168,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
bool is_texture_func = false;
bool is_screen_texture = false;
+ bool is_mask_texture = false;
bool texture_func_no_uv = false;
bool texture_func_returns_data = false;
@@ -1268,6 +1277,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[texture_uniform];
if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) {
is_screen_texture = true;
+ } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_MASK_TEXTURE) {
+ is_mask_texture = true;
} else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
is_depth_texture = true;
} else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) {
@@ -1300,7 +1311,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
String data_type_name = "";
- if (actions.check_multiview_samplers && (is_screen_texture || is_depth_texture || is_normal_roughness_texture)) {
+ if (actions.check_multiview_samplers && (is_screen_texture || is_mask_texture || is_depth_texture || is_normal_roughness_texture)) {
data_type_name = "multiviewSampler";
multiview_uv_needed = true;
} else {
@@ -1316,6 +1327,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[texture_uniform];
if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) {
multiview_uv_needed = true;
+ } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_MASK_TEXTURE) {
+ multiview_uv_needed = true;
} else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
multiview_uv_needed = true;
} else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) {
@@ -1548,6 +1561,8 @@ Error ShaderCompiler::compile(RS::ShaderMode p_mode, const String &p_code, Ident
r_gen_code.uses_global_textures = false;
r_gen_code.uses_screen_texture_mipmaps = false;
r_gen_code.uses_screen_texture = false;
+ r_gen_code.uses_mask_texture_mipmaps = false;
+ r_gen_code.uses_mask_texture = false;
r_gen_code.uses_depth_texture = false;
r_gen_code.uses_normal_roughness_texture = false;
diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h
index 66106d7eb734..cc6288b676fe 100644
--- a/servers/rendering/shader_compiler.h
+++ b/servers/rendering/shader_compiler.h
@@ -82,6 +82,8 @@ class ShaderCompiler {
bool uses_vertex_time = false;
bool uses_screen_texture_mipmaps = false;
bool uses_screen_texture = false;
+ bool uses_mask_texture_mipmaps = false;
+ bool uses_mask_texture = false;
bool uses_depth_texture = false;
bool uses_normal_roughness_texture = false;
};
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 5b4931edec5a..545045a9bb43 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -212,6 +212,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
"HINT_RANGE",
"HINT_INSTANCE_INDEX",
"HINT_SCREEN_TEXTURE",
+ "HINT_MASK_TEXTURE",
"HINT_NORMAL_ROUGHNESS_TEXTURE",
"HINT_DEPTH_TEXTURE",
"FILTER_NEAREST",
@@ -379,6 +380,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_HINT_ROUGHNESS_NORMAL_TEXTURE, "hint_roughness_normal", CF_UNSPECIFIED, {}, {} },
{ TK_HINT_ROUGHNESS_GRAY, "hint_roughness_gray", CF_UNSPECIFIED, {}, {} },
{ TK_HINT_SCREEN_TEXTURE, "hint_screen_texture", CF_UNSPECIFIED, {}, {} },
+ { TK_HINT_MASK_TEXTURE, "hint_mask_texture", CF_UNSPECIFIED, {}, {} },
{ TK_HINT_NORMAL_ROUGHNESS_TEXTURE, "hint_normal_roughness_texture", CF_UNSPECIFIED, {}, {} },
{ TK_HINT_DEPTH_TEXTURE, "hint_depth_texture", CF_UNSPECIFIED, {}, {} },
@@ -1157,6 +1159,9 @@ String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) {
case ShaderNode::Uniform::HINT_SCREEN_TEXTURE: {
result = "hint_screen_texture";
} break;
+ case ShaderNode::Uniform::HINT_MASK_TEXTURE: {
+ result = "hint_mask_texture";
+ } break;
case ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE: {
result = "hint_normal_roughness_texture";
} break;
@@ -5558,7 +5563,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (RendererCompositor::get_singleton()->is_xr_enabled() && is_custom_func) {
ShaderNode::Uniform::Hint hint = u->hint;
- if (hint == ShaderNode::Uniform::HINT_DEPTH_TEXTURE || hint == ShaderNode::Uniform::HINT_SCREEN_TEXTURE || hint == ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) {
+ if (hint == ShaderNode::Uniform::HINT_DEPTH_TEXTURE || hint == ShaderNode::Uniform::HINT_SCREEN_TEXTURE || hint == ShaderNode::Uniform::HINT_MASK_TEXTURE || hint == ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) {
_set_error(vformat(RTR("Unable to pass a multiview texture sampler as a parameter to custom function. Consider to sample it in the main function and then pass the vector result to it."), get_uniform_hint_name(hint)));
return nullptr;
}
@@ -8979,6 +8984,15 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f
--texture_uniforms;
--texture_binding;
} break;
+ case TK_HINT_MASK_TEXTURE: {
+ new_hint = ShaderNode::Uniform::HINT_MASK_TEXTURE;
+ --texture_uniforms;
+ --texture_binding;
+ if (String(shader_type_identifier) != "canvas_item") {
+ _set_error(vformat(RTR("'hint_mask_texture' is not supported in '%s' shaders."), shader_type_identifier));
+ return ERR_PARSE_ERROR;
+ }
+ } break;
case TK_HINT_NORMAL_ROUGHNESS_TEXTURE: {
new_hint = ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE;
--texture_uniforms;
@@ -10770,6 +10784,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
options.push_back("hint_roughness_normal");
options.push_back("hint_roughness_r");
options.push_back("hint_screen_texture");
+ options.push_back("hint_mask_texture");
options.push_back("hint_normal_roughness_texture");
options.push_back("hint_depth_texture");
options.push_back("source_color");
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index edac819a1e1d..cda84f43330d 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -177,6 +177,7 @@ class ShaderLanguage {
TK_HINT_RANGE,
TK_HINT_INSTANCE_INDEX,
TK_HINT_SCREEN_TEXTURE,
+ TK_HINT_MASK_TEXTURE,
TK_HINT_NORMAL_ROUGHNESS_TEXTURE,
TK_HINT_DEPTH_TEXTURE,
TK_FILTER_NEAREST,
@@ -672,6 +673,7 @@ class ShaderLanguage {
HINT_DEFAULT_TRANSPARENT,
HINT_ANISOTROPY,
HINT_SCREEN_TEXTURE,
+ HINT_MASK_TEXTURE,
HINT_NORMAL_ROUGHNESS_TEXTURE,
HINT_DEPTH_TEXTURE,
HINT_MAX
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index dd3491f62ced..78cc9f888b94 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -3285,6 +3285,7 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_ONLY);
BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_AND_DRAW);
BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_TRANSPARENT);
+ BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_SUBTRACT);
/* CANVAS LIGHT */
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index e15dba43536f..f0d156d9bd8d 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1504,10 +1504,12 @@ class RenderingServer : public Object {
virtual void canvas_item_set_visibility_notifier(RID p_item, bool p_enable, const Rect2 &p_area, const Callable &p_enter_callbable, const Callable &p_exit_callable) = 0;
enum CanvasGroupMode {
+ // Note: The following need to match the order of CanvasItem::ClipChildrenMode
CANVAS_GROUP_MODE_DISABLED,
CANVAS_GROUP_MODE_CLIP_ONLY,
CANVAS_GROUP_MODE_CLIP_AND_DRAW,
CANVAS_GROUP_MODE_TRANSPARENT,
+ CANVAS_GROUP_MODE_SUBTRACT,
};
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;