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;