diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 5a73ad34ba..299365ddfd 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -457,6 +457,13 @@ [b]Note:[/b] The equivalent node is [CanvasItem]. + + + + + Ends draw call pre-sorting. See [method canvas_item_set_presort_level]. + + @@ -618,6 +625,14 @@ Sets a parent [CanvasItem] to the [CanvasItem]. The item will inherit transform, modulation and visibility from its parent, like [CanvasItem] nodes in the scene tree. + + + + + + Starts draw call pre-sorting or changes the pre-sorting level. All subsequent draw calls until [method canvas_item_flush_presort] is called are sorted according to the [param order] (in ascending order) and texture usage, to ensure draw call batching. The original call order is not preserved, except [method canvas_item_add_set_transform], [method canvas_item_add_clip_ignore], and [method canvas_item_add_animation_slice] effects. Draw calls made before the first [method canvas_item_set_presort_level] call or after [method canvas_item_flush_presort] is called are also not affected. [param order] should be greater or equal to zero. + + diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 3b570522c9..cd018933b0 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -446,7 +446,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } if (ci->skeleton.is_valid()) { - const Item::Command *c = ci->commands; + const Item::Command *c = ci->first_command; while (c) { if (c->type == Item::Command::TYPE_MESH) { @@ -876,7 +876,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend } const RSE::CanvasItemTextureRepeat base_texture_repeat = p_item->texture_repeat == RSE::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? state.default_repeat : p_item->texture_repeat; - const Item::Command *c = p_item->commands; + const Item::Command *c = p_item->first_command; while (c) { if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { c = c->next; diff --git a/editor/script/script_text_editor.cpp b/editor/script/script_text_editor.cpp index b01b678d0f..87ae2b2dde 100644 --- a/editor/script/script_text_editor.cpp +++ b/editor/script/script_text_editor.cpp @@ -586,14 +586,18 @@ Array ScriptTextEditor::_inline_object_parse(const String &p_text) { } void ScriptTextEditor::_inline_object_draw(const Dictionary &p_info, const Rect2 &p_rect) { + // Note: this method uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. See TextEdit::DrawStep. if (_is_valid_color_info(p_info)) { Rect2 col_rect = p_rect.grow(-4); if (color_alpha_texture.is_null()) { color_alpha_texture = inline_color_picker->get_theme_icon("sample_bg", "ColorPicker"); } RID text_ci = code_editor->get_text_editor()->get_text_canvas_item(); + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, TextEdit::DrawStep::DRAW_STEP_TEXT_OUTLINE); RS::get_singleton()->canvas_item_add_rect(text_ci, p_rect.grow(-3), Color(1, 1, 1)); + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, TextEdit::DrawStep::DRAW_STEP_TEXT); color_alpha_texture->draw_rect(text_ci, col_rect); + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, TextEdit::DrawStep::DRAW_STEP_TEXT_OVERLAY); RS::get_singleton()->canvas_item_add_rect(text_ci, col_rect, Color(p_info["color"])); } } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 776afd599f..0808d13288 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1525,6 +1525,7 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const { } void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { + // Note: this method uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. See TextEdit::DrawStep. bool hovering = get_hovered_gutter() == Vector2i(main_gutter, p_line); RID ci = get_text_canvas_item(); if (draw_breakpoints && theme_cache.breakpoint_icon.is_valid()) { @@ -1715,6 +1716,7 @@ int CodeEdit::get_line_numbers_min_digits() const { } void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { + // Note: this method uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. See TextEdit::DrawStep. if (!Rect2(Vector2(0, 0), get_size()).intersects(p_region)) { return; } @@ -1777,6 +1779,7 @@ bool CodeEdit::is_drawing_fold_gutter() const { } void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region) { + // Note: this method uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. See TextEdit::DrawStep. if (!can_fold_line(p_line) && !is_line_folded(p_line)) { set_line_gutter_clickable(p_line, fold_gutter, false); return; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 4de80ad740..4651afdc20 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -804,6 +804,7 @@ void Label::_notification(int p_what) { int visible_glyphs = total_glyphs * visible_ratio; int line_index = 0; + // Note: part of this method between this comment and `canvas_item_flush_presort` call uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. for (int p = 0; p < paragraphs.size(); p++) { const Paragraph ¶ = paragraphs[p]; if (line_index + para.lines_rid.size() <= lines_skipped) { @@ -843,12 +844,17 @@ void Label::_notification(int p_what) { int processed_glyphs_step = 0; // Draw shadow outline. + int draw_step = 0; if (font_shadow_color.a != 0 && shadow_outline_size > 0) { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, draw_step); + draw_step++; draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow_outline, shadow_outline_size, shadow_ofs); } // Draw shadow. if (font_shadow_color.a > 0) { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, draw_step); + draw_step++; draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow, shadow_ofs); } @@ -859,9 +865,12 @@ void Label::_notification(int p_what) { for (int draw_iteration_index = draw_iterations - 1; draw_iteration_index >= 0; --draw_iteration_index) { LabelSettings::StackedShadowData stacked_shadow_data = stacked_shadow_datas[draw_iteration_index]; if (stacked_shadow_data.outline_size > 0) { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, draw_step); + draw_step++; draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow_outline, stacked_shadow_data.outline_size, stacked_shadow_data.offset); } - + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, draw_step); + draw_step++; draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow, stacked_shadow_data.offset); } } @@ -885,6 +894,8 @@ void Label::_notification(int p_what) { if (stacked_outline_data.size <= 0) { continue; } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, draw_step); + draw_step++; draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_outline_data.color, draw_glyph_outline, stacked_outline_draw_size); stacked_outline_draw_size -= stacked_outline_data.size; } @@ -892,11 +903,15 @@ void Label::_notification(int p_what) { // Draw outline. if (outline_size > 0 && font_outline_color.a != 0) { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, draw_step); + draw_step++; draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_outline_color, draw_glyph_outline, outline_size); } // Draw text. { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, draw_step); + draw_step++; draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_color, draw_glyph); } @@ -907,6 +922,7 @@ void Label::_notification(int p_what) { line_index += para.lines_rid.size(); } } + RenderingServer::get_singleton()->canvas_item_flush_presort(ci); } break; case NOTIFICATION_THEME_CHANGED: { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index b6209ab35f..1d436b9b78 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1519,11 +1519,13 @@ void LineEdit::_notification(int p_what) { int gl_size = TS->shaped_text_get_glyph_count(text_rid); // Draw text. + // Note: part of this method between this comment and `canvas_item_flush_presort` call uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. ofs.y += TS->shaped_text_get_ascent(text_rid); Color font_outline_color = theme_cache.font_outline_color; int outline_size = theme_cache.font_outline_size; if (outline_size > 0 && font_outline_color.a > 0) { Vector2 oofs = ofs; + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, DRAW_STEP_OUTLINE); for (int i = 0; i < gl_size; i++) { for (int j = 0; j < glyphs[i].repeat; j++) { if (std::ceil(oofs.x) >= x_ofs && (oofs.x + glyphs[i].advance) <= ofs_max) { @@ -1538,6 +1540,7 @@ void LineEdit::_notification(int p_what) { } } } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, DRAW_STEP_TEXT); for (int i = 0; i < gl_size; i++) { bool selected = selection.enabled && glyphs[i].start >= selection.begin && glyphs[i].end <= selection.end; for (int j = 0; j < glyphs[i].repeat; j++) { @@ -1554,6 +1557,7 @@ void LineEdit::_notification(int p_what) { break; } } + RenderingServer::get_singleton()->canvas_item_flush_presort(ci); // Draw carets. ofs.x = x_ofs + scroll_offset; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index f1e8477eed..303bde6162 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -91,6 +91,11 @@ public: }; private: + enum LEDrawStep { + DRAW_STEP_OUTLINE, + DRAW_STEP_TEXT, + }; + HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; bool editing = false; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index c60934be26..ac1bdef9b0 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1078,7 +1078,8 @@ void RichTextLabel::_update_table_size(ItemTable *p_table) { } } -int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs) { +int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs, int p_draw_step) { + // Note: this method uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. ERR_FAIL_NULL_V(p_frame, 0); ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), 0); @@ -1109,8 +1110,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o bool skip_dc = (trim_chars && l.char_offset > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs)); if (!skip_dc) { if (l.dc_ol_size > 0) { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_OUTLINE); l.text_buf->draw_dropcap_outline(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_ol_size, l.dc_ol_color); } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_TEXT); l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color); } @@ -1178,20 +1181,24 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Color font_outline_color = l.prefix_outline_color == Color(0, 0, 0, 0) ? _find_outline_color(l.from, p_base_color) : l.prefix_outline_color; Color font_shadow_color = p_font_shadow_color * Color(1, 1, 1, font_color.a); if (rtl) { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_OUTLINE); if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) { l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x + length, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); } if (outline_size > 0 && font_outline_color.a != 0.0) { l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x + length, 0), outline_size, font_outline_color); } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_TEXT); l.text_prefix->draw(ci, p_ofs + Vector2(off.x + length, 0), font_color); } else { + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_OUTLINE); if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) { l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); } if (outline_size > 0 && font_outline_color.a != 0.0) { l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), outline_size, font_outline_color); } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_TEXT); l.text_prefix->draw(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), font_color); } } @@ -1225,7 +1232,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } int processed_glyphs_step = 0; - for (int step = DRAW_STEP_BACKGROUND; step < DRAW_STEP_MAX; step++) { + for (int step = DRAW_STEP_BACKGROUND; step < DRAW_STEP_TEXT_MAX; step++) { if (step == DRAW_STEP_TEXT) { // Draw inlined objects. Array objects = TS->shaped_text_get_objects(rid); @@ -1246,6 +1253,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o switch (it->type) { case ITEM_IMAGE: { ItemImage *img = static_cast(it); + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_TEXT); if (img->pad) { Size2 pad_size = rect.size.min(img->image->get_size()); Vector2 pad_off = (rect.size - pad_size) / 2; @@ -1274,7 +1282,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o table_ofs -= Vector2(h_separation * 0.5, v_separation * 0.5).floor(); } - bool right_has_border = false; int idx = 0; for (Item *E : table->subitems) { ItemFrame *frame = static_cast(E); @@ -1301,29 +1308,19 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o row_bg = frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg; } if (row_bg.a > 0.0) { - if (right_has_border) { - // To prevent the border of the right cell from being covered. - cell_rect.size.x -= 1; - draw_rect(cell_rect, row_bg, true); - cell_rect.size.x += 1; - } else { - draw_rect(cell_rect, row_bg, true); - } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_CELL_BACKGROUND); + draw_rect(cell_rect, row_bg, true); } - right_has_border = false; - Color bc = frame->border != Color(0, 0, 0, 0) ? frame->border : border; if (bc.a > 0.0) { - if (rtl && col < col_count - 1) { - right_has_border = true; - } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + DRAW_STEP_CELL_BORDER); draw_rect(cell_rect, bc, false); } } for (int j = 0; j < (int)frame->lines.size(); j++) { - _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, 0, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs); + _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, 0, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs, p_draw_step + DRAW_STEP_MAX); } idx++; } @@ -1334,6 +1331,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } } + RenderingServer::get_singleton()->canvas_item_set_presort_level(ci, p_draw_step + step); Vector2 off_step = off; processed_glyphs_step = r_processed_glyphs; @@ -2839,10 +2837,11 @@ void RichTextLabel::_notification(int p_what) { // New cache draw. Point2 ofs = text_rect.get_position() + Vector2(0, vbegin + main->lines[from_line].offset.y - vofs); int processed_glyphs = 0; + // Note: this loop uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. while (ofs.y < size.height - v_limit && from_line < to_line) { MutexLock lock(main->lines[from_line].text_buf->get_mutex()); - int drawn_lines = _draw_line(main, from_line, ofs, text_rect.size.x, vsep, theme_cache.default_color, theme_cache.outline_size, theme_cache.font_outline_color, theme_cache.font_shadow_color, theme_cache.shadow_outline_size, shadow_ofs, processed_glyphs); + int drawn_lines = _draw_line(main, from_line, ofs, text_rect.size.x, vsep, theme_cache.default_color, theme_cache.outline_size, theme_cache.font_outline_color, theme_cache.font_shadow_color, theme_cache.shadow_outline_size, shadow_ofs, processed_glyphs, 0); visible_line_count += drawn_lines; if (drawn_lines > 0) { visible_paragraph_count++; @@ -2850,6 +2849,7 @@ void RichTextLabel::_notification(int p_what) { ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * (theme_cache.line_separation + vsep) + (theme_cache.paragraph_separation); from_line++; } + RenderingServer::get_singleton()->canvas_item_flush_presort(ci); if (scroll_follow_visible_characters && scroll_active) { scroll_visible = follow_vc_pos > 0; if (scroll_visible) { diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 0c1fb8a05d..b8024b3a76 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -57,6 +57,9 @@ class RichTextLabel : public Control { DRAW_STEP_OUTLINE, DRAW_STEP_TEXT, DRAW_STEP_FOREGROUND, + DRAW_STEP_TEXT_MAX, + DRAW_STEP_CELL_BACKGROUND = DRAW_STEP_TEXT_MAX, + DRAW_STEP_CELL_BORDER, DRAW_STEP_MAX, }; @@ -666,7 +669,7 @@ private: void _update_table_size(ItemTable *p_table); void _update_line_font(ItemFrame *p_frame, int p_line, const Ref &p_base_font, int p_base_font_size); - int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs); + int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs, int p_draw_step); float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false, bool p_meta = false); void _accessibility_update_line(RID p_id, ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index cd092bd4ff..4786d7b0f9 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1336,6 +1336,7 @@ void TextEdit::_notification(int p_what) { _draw_guidelines(); // Draw main text. + // Note: part of this method between this comment and `canvas_item_flush_presort` call uses pre-sorted draw, draw call order is ignored, use `canvas_item_set_presort_level` to specify draw order. line_drawing_cache.clear(); int row_height = draw_placeholder ? placeholder_line_height + theme_cache.line_spacing : get_line_height(); int line = first_vis_line; @@ -1408,6 +1409,7 @@ void TextEdit::_notification(int p_what) { break; } + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_LINE_BACKGROUND); if (text.get_line_background_color(line).a > 0.0) { if (rtl) { RS::get_singleton()->canvas_item_add_rect(text_ci, Rect2(size.width - xmargin_end, ofs_y, xmargin_end - xmargin_beg, row_height), text.get_line_background_color(line)); @@ -1417,6 +1419,7 @@ void TextEdit::_notification(int p_what) { } // Draw current line highlight. + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_LINE_HIGHLIGHT); if (highlight_current_line && highlighted_lines.has(Pair(line, line_wrap_index))) { if (rtl) { RS::get_singleton()->canvas_item_add_rect(text_ci, Rect2(size.width - xmargin_end, ofs_y, xmargin_end, row_height), theme_cache.current_line_color); @@ -1451,8 +1454,10 @@ void TextEdit::_notification(int p_what) { int yofs = ofs_y + (row_height - tl->get_size().y) / 2; if (theme_cache.outline_size > 0 && theme_cache.outline_color.a > 0) { + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT_OUTLINE); tl->draw_outline(text_ci, Point2(gutter_offset, yofs), theme_cache.outline_size, theme_cache.outline_color); } + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT); tl->draw(text_ci, Point2(gutter_offset, yofs), get_line_gutter_item_color(line, g)); } break; case GUTTER_TYPE_ICON: { @@ -1484,7 +1489,7 @@ void TextEdit::_notification(int p_what) { if (rtl) { gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x; } - + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT); icon->draw_rect(text_ci, gutter_rect, false, get_line_gutter_item_color(line, g)); } break; case GUTTER_TYPE_CUSTOM: { @@ -1493,6 +1498,7 @@ void TextEdit::_notification(int p_what) { if (rtl) { gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x; } + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT); gutter.custom_draw_callback.call(line, g, Rect2(gutter_rect)); } } break; @@ -1526,6 +1532,7 @@ void TextEdit::_notification(int p_what) { // Draw selections. float char_w = theme_cache.font->get_char_size(' ', theme_cache.font_size).width; + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT_SELECTION); for (int c = 0; c < get_caret_count(); c++) { if (!clipped && has_selection(c) && line >= get_selection_from_line(c) && line <= get_selection_to_line(c)) { int sel_from = (line > get_selection_from_line(c)) ? TS->shaped_text_get_range(rid).x : get_selection_from_column(c); @@ -1568,6 +1575,7 @@ void TextEdit::_notification(int p_what) { } int start = TS->shaped_text_get_range(rid).x; + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_SEARCH_HIGHLIGHT); if (!clipped && !search_text.is_empty()) { // Search highlight int search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0); int search_text_len = search_text.length(); @@ -1654,6 +1662,7 @@ void TextEdit::_notification(int p_what) { int last_visible_char = TS->shaped_text_get_range(rid).x; float char_ofs = 0; + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT_OUTLINE); if (theme_cache.outline_size > 0 && theme_cache.outline_color.a > 0) { for (int j = 0; j < gl_size; j++) { for (int k = 0; k < glyphs[j].repeat; k++) { @@ -1680,6 +1689,7 @@ void TextEdit::_notification(int p_what) { } } + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT); for (int j = 0; j < gl_size; j++) { for (const Pair &color_data : color_map) { if (color_data.first <= glyphs[j].start) { @@ -1770,6 +1780,7 @@ void TextEdit::_notification(int p_what) { cache_entry.first_visible_chars.push_back(first_visible_char); cache_entry.last_visible_chars.push_back(last_visible_char); + RenderingServer::get_singleton()->canvas_item_set_presort_level(text_ci, DRAW_STEP_TEXT_OVERLAY); // is_line_folded if (line_wrap_index == line_wrap_amount && line < text.size() - 1 && _is_line_hidden(line + 1)) { int xofs = char_ofs + char_margin + (_get_folded_eol_icon()->get_width() / 2); @@ -1954,6 +1965,7 @@ void TextEdit::_notification(int p_what) { line_drawing_cache[line] = cache_entry; } } + RenderingServer::get_singleton()->canvas_item_flush_presort(text_ci); if (has_focus()) { _update_ime_window_position(); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 8470cccd39..a075a9b449 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -121,6 +121,16 @@ public: SEARCH_BACKWARDS = 4 }; + enum DrawStep { + DRAW_STEP_LINE_BACKGROUND, + DRAW_STEP_LINE_HIGHLIGHT, + DRAW_STEP_TEXT_SELECTION, + DRAW_STEP_SEARCH_HIGHLIGHT, + DRAW_STEP_TEXT_OUTLINE, + DRAW_STEP_TEXT, + DRAW_STEP_TEXT_OVERLAY, + }; + private: struct GutterInfo { GutterType type = GutterType::GUTTER_TYPE_STRING; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 9a0c442d8f..b4ebb89a9b 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -222,7 +222,7 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * // We have two choices now, if user has drawn something, we must assume users wants to draw the "mask", so compute the size based on this. // If nothing has been drawn, we just take it over and draw it ourselves. - if (ci->canvas_group->fit_empty && (ci->commands == nullptr || (ci->commands->next == nullptr && ci->commands->type == RendererCanvasCull::Item::Command::TYPE_RECT && (static_cast(ci->commands)->flags & RendererCanvasRender::CANVAS_RECT_IS_GROUP)))) { + if (ci->canvas_group->fit_empty && (ci->first_command == nullptr || (ci->first_command->next == nullptr && ci->first_command->type == RendererCanvasCull::Item::Command::TYPE_RECT && (static_cast(ci->first_command)->flags & RendererCanvasRender::CANVAS_RECT_IS_GROUP)))) { // No commands, or sole command is the one used to draw, so we (re)create the draw command. ci->clear(); @@ -257,14 +257,14 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * } } - if (((ci->commands != nullptr || ci->visibility_notifier) && p_clip_rect.intersects(p_global_rect, true)) || ci->vp_render || ci->copy_back_buffer) { + if (((ci->first_command != nullptr || ci->visibility_notifier) && p_clip_rect.intersects(p_global_rect, true)) || ci->vp_render || ci->copy_back_buffer) { // Something to draw? if (ci->update_when_visible) { RenderingServerDefault::redraw_request(); } - if (ci->commands != nullptr || ci->copy_back_buffer) { + if (ci->first_command != nullptr || ci->copy_back_buffer) { ci->final_transform = !ci->use_identity_transform ? p_transform : _current_camera_transform; ci->final_modulate = p_modulate * ci->self_modulate; ci->global_rect_cache = p_global_rect; @@ -330,6 +330,12 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 return; } +#ifdef DEBUG_ENABLED + if (!ci->presort_commands.is_empty()) { + WARN_PRINT(vformat("Presort list for canvas item %x was not flushed!", ci->self.get_id())); + } +#endif + Rect2 rect = ci->get_rect(); if (ci->visibility_notifier) { @@ -460,7 +466,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 } } else { RendererCanvasRender::Item *canvas_group_from = nullptr; - bool use_canvas_group = ci->canvas_group != nullptr && (ci->canvas_group->fit_empty || ci->commands != nullptr); + bool use_canvas_group = ci->canvas_group != nullptr && (ci->canvas_group->fit_empty || ci->first_command != nullptr); if (use_canvas_group) { int zidx = p_z - RSE::CANVAS_ITEM_Z_MIN; canvas_group_from = r_z_last_list[zidx]; @@ -470,7 +476,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 } } else { RendererCanvasRender::Item *canvas_group_from = nullptr; - bool use_canvas_group = ci->canvas_group != nullptr && (ci->canvas_group->fit_empty || ci->commands != nullptr); + bool use_canvas_group = ci->canvas_group != nullptr && (ci->canvas_group->fit_empty || ci->first_command != nullptr); if (use_canvas_group) { int zidx = p_z - RSE::CANVAS_ITEM_Z_MIN; canvas_group_from = r_z_last_list[zidx]; @@ -744,12 +750,89 @@ float RendererCanvasCull::canvas_item_get_compensated_antialiasing_width(float p return p_width; } +void RendererCanvasCull::canvas_item_set_presort_level(RID p_item, int64_t p_order) { + Item *canvas_item = canvas_item_owner.get_or_null(p_item); + ERR_FAIL_NULL(canvas_item); + ERR_FAIL_COND(p_order < 0); + + if (canvas_item->presort_level == -1) { + canvas_item->presort_prev_command = canvas_item->last_command; + if (!canvas_item->presort_prev_state.is_base_state()) { + canvas_item->presort_states.push_back(canvas_item->presort_prev_state); + canvas_item->presort_state = 0; + } + } + canvas_item->presort_level = p_order; +} + +void RendererCanvasCull::_update_presort_state(Item *p_canvas_item, int32_t p_old_state, int32_t p_new_state, Item::Command **r_prev_command) { + const Item::PresortState &old_state = (p_old_state >= 0) ? p_canvas_item->presort_states[p_old_state] : Item::PresortState(); + const Item::PresortState &new_state = (p_new_state >= 0) ? p_canvas_item->presort_states[p_new_state] : Item::PresortState(); + if (old_state.transform != new_state.transform) { + Item::CommandTransform *tr = p_canvas_item->alloc_command(false); + if (tr) { + tr->xform = new_state.transform; + _insert_command(p_canvas_item, r_prev_command, tr); + } + } + if (old_state.clip_ignore != new_state.clip_ignore) { + Item::CommandClipIgnore *ci = p_canvas_item->alloc_command(false); + if (ci) { + ci->ignore = new_state.clip_ignore; + _insert_command(p_canvas_item, r_prev_command, ci); + } + } + if (old_state.animation_length != new_state.animation_length || old_state.slice_begin != new_state.slice_begin || old_state.slice_end != new_state.slice_end || old_state.offset != new_state.offset) { + Item::CommandAnimationSlice *as = p_canvas_item->alloc_command(false); + if (as) { + as->animation_length = new_state.animation_length; + as->slice_begin = new_state.slice_begin; + as->slice_end = new_state.slice_end; + as->offset = new_state.offset; + _insert_command(p_canvas_item, r_prev_command, as); + } + } +} + +void RendererCanvasCull::canvas_item_flush_presort(RID p_item) { + Item *canvas_item = canvas_item_owner.get_or_null(p_item); + ERR_FAIL_NULL(canvas_item); + + canvas_item->presort_commands.sort_custom(); + if (!canvas_item->presort_commands.is_empty()) { + Item::Command *prev_command = canvas_item->presort_prev_command; + int32_t start_presort_state = canvas_item->presort_prev_state.is_base_state() ? -1 : 0; + int32_t prev_presort_state = start_presort_state; + for (Item::PresortData &pd : canvas_item->presort_commands) { + if (prev_presort_state != pd.state) { + _update_presort_state(canvas_item, prev_presort_state, pd.state, &prev_command); + prev_presort_state = pd.state; + } + _insert_command(canvas_item, &prev_command, pd.command); + } + if (start_presort_state != prev_presort_state) { + _update_presort_state(canvas_item, prev_presort_state, start_presort_state, &prev_command); + } + prev_command->next = nullptr; + canvas_item->last_command = prev_command; + } + canvas_item->presort_commands.clear(); + canvas_item->presort_states.clear(); + canvas_item->presort_prev_command = nullptr; + canvas_item->presort_level = -1; + canvas_item->presort_state = -1; + canvas_item->presort_prev_state = Item::PresortState(); +} + void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width, bool p_antialiased) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); Item::CommandPrimitive *line = canvas_item->alloc_command(); ERR_FAIL_NULL(line); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ line, canvas_item->presort_level, canvas_item->presort_state }); + } Vector2 diff = (p_from - p_to); Vector2 dir = diff.orthogonal().normalized(); @@ -806,6 +889,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *left_border = canvas_item->alloc_command(); ERR_FAIL_NULL(left_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ left_border, canvas_item->presort_level, canvas_item->presort_state }); + } left_border->points[0] = begin_left; left_border->points[1] = begin_left + border; @@ -822,6 +908,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *right_border = canvas_item->alloc_command(); ERR_FAIL_NULL(right_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ right_border, canvas_item->presort_level, canvas_item->presort_state }); + } right_border->points[0] = begin_right; right_border->points[1] = begin_right - border; @@ -838,6 +927,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *top_border = canvas_item->alloc_command(); ERR_FAIL_NULL(top_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ top_border, canvas_item->presort_level, canvas_item->presort_state }); + } top_border->points[0] = begin_left; top_border->points[1] = begin_left + border2; @@ -854,6 +946,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *bottom_border = canvas_item->alloc_command(); ERR_FAIL_NULL(bottom_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ bottom_border, canvas_item->presort_level, canvas_item->presort_state }); + } bottom_border->points[0] = end_left; bottom_border->points[1] = end_left - border2; @@ -870,6 +965,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *top_left_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(top_left_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ top_left_corner, canvas_item->presort_level, canvas_item->presort_state }); + } top_left_corner->points[0] = begin_left; top_left_corner->points[1] = begin_left + border2; @@ -886,6 +984,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *top_right_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(top_right_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ top_right_corner, canvas_item->presort_level, canvas_item->presort_state }); + } top_right_corner->points[0] = begin_right; top_right_corner->points[1] = begin_right + border2; @@ -902,6 +1003,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *bottom_left_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(bottom_left_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ bottom_left_corner, canvas_item->presort_level, canvas_item->presort_state }); + } bottom_left_corner->points[0] = end_left; bottom_left_corner->points[1] = end_left - border2; @@ -918,6 +1022,9 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, { Item::CommandPrimitive *bottom_right_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(bottom_right_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ bottom_right_corner, canvas_item->presort_level, canvas_item->presort_state }); + } bottom_right_corner->points[0] = end_right; bottom_right_corner->points[1] = end_right - border2; @@ -989,6 +1096,9 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vectoralloc_command(); ERR_FAIL_NULL(pline); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ pline, canvas_item->presort_level, canvas_item->presort_state }); + } if (p_antialiased) { p_width = canvas_item_get_compensated_antialiasing_width(p_width); @@ -1065,9 +1175,15 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vectoralloc_command(); ERR_FAIL_NULL(pline_left); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ pline_left, canvas_item->presort_level, canvas_item->presort_state }); + } Item::CommandPolygon *pline_right = canvas_item->alloc_command(); ERR_FAIL_NULL(pline_right); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ pline_right, canvas_item->presort_level, canvas_item->presort_state }); + } PackedColorArray colors_left; PackedVector2Array points_left; @@ -1274,6 +1390,10 @@ void RendererCanvasCull::canvas_item_add_multiline(RID p_item, const Vectoralloc_command(); ERR_FAIL_NULL(pline); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ pline, canvas_item->presort_level, canvas_item->presort_state }); + } + pline->primitive = RSE::PRIMITIVE_LINES; pline->polygon.create(Vector(), p_points, colors); } else { @@ -1306,6 +1426,10 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_NULL(rect); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ rect, canvas_item->presort_level, canvas_item->presort_state }); + } + rect->modulate = p_color; rect->rect = rect_adjusted; @@ -1336,6 +1460,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *left_border = canvas_item->alloc_command(); ERR_FAIL_NULL(left_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ left_border, canvas_item->presort_level, canvas_item->presort_state }); + } left_border->points[0] = begin_left; left_border->points[1] = begin_left + border; @@ -1352,6 +1479,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *right_border = canvas_item->alloc_command(); ERR_FAIL_NULL(right_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ right_border, canvas_item->presort_level, canvas_item->presort_state }); + } right_border->points[0] = begin_right; right_border->points[1] = begin_right - border; @@ -1368,6 +1498,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *top_border = canvas_item->alloc_command(); ERR_FAIL_NULL(top_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ top_border, canvas_item->presort_level, canvas_item->presort_state }); + } top_border->points[0] = begin_left; top_border->points[1] = begin_left + border2; @@ -1384,6 +1517,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *bottom_border = canvas_item->alloc_command(); ERR_FAIL_NULL(bottom_border); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ bottom_border, canvas_item->presort_level, canvas_item->presort_state }); + } bottom_border->points[0] = end_left; bottom_border->points[1] = end_left - border2; @@ -1400,6 +1536,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *top_left_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(top_left_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ top_left_corner, canvas_item->presort_level, canvas_item->presort_state }); + } top_left_corner->points[0] = begin_left; top_left_corner->points[1] = begin_left + border2; @@ -1416,6 +1555,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *top_right_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(top_right_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ top_right_corner, canvas_item->presort_level, canvas_item->presort_state }); + } top_right_corner->points[0] = begin_right; top_right_corner->points[1] = begin_right + border2; @@ -1432,6 +1574,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *bottom_left_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(bottom_left_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ bottom_left_corner, canvas_item->presort_level, canvas_item->presort_state }); + } bottom_left_corner->points[0] = end_left; bottom_left_corner->points[1] = end_left - border2; @@ -1448,6 +1593,9 @@ void RendererCanvasCull::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c { Item::CommandPrimitive *bottom_right_corner = canvas_item->alloc_command(); ERR_FAIL_NULL(bottom_right_corner); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ bottom_right_corner, canvas_item->presort_level, canvas_item->presort_state }); + } bottom_right_corner->points[0] = end_right; bottom_right_corner->points[1] = end_right - border2; @@ -1481,6 +1629,9 @@ void RendererCanvasCull::canvas_item_add_ellipse(RID p_item, const Point2 &p_pos { Item::CommandPolygon *ellipse = canvas_item->alloc_command(); ERR_FAIL_NULL(ellipse); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ ellipse, canvas_item->presort_level, canvas_item->presort_state }); + } ellipse->primitive = RSE::PRIMITIVE_TRIANGLES; @@ -1526,6 +1677,10 @@ void RendererCanvasCull::canvas_item_add_ellipse(RID p_item, const Point2 &p_pos Item::CommandPolygon *feather = canvas_item->alloc_command(); ERR_FAIL_NULL(feather); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ feather, canvas_item->presort_level, canvas_item->presort_state }); + } + feather->primitive = RSE::PRIMITIVE_TRIANGLE_STRIP; Color transparent = Color(p_color, 0.0); @@ -1573,6 +1728,10 @@ void RendererCanvasCull::canvas_item_add_texture_rect(RID p_item, const Rect2 &p Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_NULL(rect); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ rect, canvas_item->presort_level, canvas_item->presort_state }); + } + rect->modulate = p_modulate; rect->rect = p_rect; rect->flags = 0; @@ -1604,6 +1763,10 @@ void RendererCanvasCull::canvas_item_add_msdf_texture_rect_region(RID p_item, co Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_NULL(rect); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ rect, canvas_item->presort_level, canvas_item->presort_state }); + } + rect->modulate = p_modulate; rect->rect = p_rect; @@ -1638,6 +1801,10 @@ void RendererCanvasCull::canvas_item_add_lcd_texture_rect_region(RID p_item, con Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_NULL(rect); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ rect, canvas_item->presort_level, canvas_item->presort_state }); + } + rect->modulate = p_modulate; rect->rect = p_rect; @@ -1670,6 +1837,10 @@ void RendererCanvasCull::canvas_item_add_texture_rect_region(RID p_item, const R Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_NULL(rect); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ rect, canvas_item->presort_level, canvas_item->presort_state }); + } + rect->modulate = p_modulate; rect->rect = p_rect; @@ -1711,6 +1882,9 @@ void RendererCanvasCull::canvas_item_add_nine_patch(RID p_item, const Rect2 &p_r Item::CommandNinePatch *style = canvas_item->alloc_command(); ERR_FAIL_NULL(style); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ style, canvas_item->presort_level, canvas_item->presort_state }); + } style->texture = p_texture; @@ -1735,6 +1909,9 @@ void RendererCanvasCull::canvas_item_add_primitive(RID p_item, const Vectoralloc_command(); ERR_FAIL_NULL(prim); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ prim, canvas_item->presort_level, canvas_item->presort_state }); + } for (int i = 0; i < p_points.size(); i++) { prim->points[i] = p_points[i]; @@ -1771,7 +1948,12 @@ void RendererCanvasCull::canvas_item_add_polygon(RID p_item, const Vectoralloc_command(); ERR_FAIL_NULL(polygon); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ polygon, canvas_item->presort_level, canvas_item->presort_state }); + } + polygon->primitive = RSE::PRIMITIVE_TRIANGLES; + polygon->texture = p_texture; polygon->polygon.create(indices, p_points, p_colors, p_uvs); } @@ -1789,6 +1971,9 @@ void RendererCanvasCull::canvas_item_add_triangle_array(RID p_item, const Vector Item::CommandPolygon *polygon = canvas_item->alloc_command(); ERR_FAIL_NULL(polygon); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ polygon, canvas_item->presort_level, canvas_item->presort_state }); + } polygon->texture = p_texture; @@ -1800,10 +1985,28 @@ void RendererCanvasCull::canvas_item_add_triangle_array(RID p_item, const Vector void RendererCanvasCull::canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); + if (canvas_item->presort_level >= 0) { + Item::PresortState st; + if (canvas_item->presort_state >= 0) { + st = canvas_item->presort_states[canvas_item->presort_state]; + } + if (st.transform == p_transform) { + return; + } + st.transform = p_transform; + if (st.is_base_state()) { + canvas_item->presort_state = -1; + } else { + canvas_item->presort_states.push_back(st); + canvas_item->presort_state = canvas_item->presort_states.size() - 1; + } + return; + } Item::CommandTransform *tr = canvas_item->alloc_command(); ERR_FAIL_NULL(tr); tr->xform = p_transform; + canvas_item->presort_prev_state.transform = p_transform; } void RendererCanvasCull::canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform, const Color &p_modulate, RID p_texture) { @@ -1813,6 +2016,10 @@ void RendererCanvasCull::canvas_item_add_mesh(RID p_item, const RID &p_mesh, con Item::CommandMesh *m = canvas_item->alloc_command(); ERR_FAIL_NULL(m); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ m, canvas_item->presort_level, canvas_item->presort_state }); + } + m->mesh = p_mesh; if (canvas_item->skeleton.is_valid()) { m->mesh_instance = RSG::mesh_storage->mesh_instance_create(p_mesh); @@ -1831,6 +2038,10 @@ void RendererCanvasCull::canvas_item_add_particles(RID p_item, RID p_particles, Item::CommandParticles *part = canvas_item->alloc_command(); ERR_FAIL_NULL(part); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ part, canvas_item->presort_level, canvas_item->presort_state }); + } + part->particles = p_particles; part->texture = p_texture; @@ -1845,6 +2056,10 @@ void RendererCanvasCull::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p Item::CommandMultiMesh *mm = canvas_item->alloc_command(); ERR_FAIL_NULL(mm); + if (canvas_item->presort_level >= 0) { + canvas_item->presort_commands.push_back({ mm, canvas_item->presort_level, canvas_item->presort_state }); + } + mm->multimesh = p_mesh; mm->texture = p_texture; @@ -1853,15 +2068,53 @@ void RendererCanvasCull::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p void RendererCanvasCull::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); + if (canvas_item->presort_level >= 0) { + Item::PresortState st; + if (canvas_item->presort_state >= 0) { + st = canvas_item->presort_states[canvas_item->presort_state]; + } + if (st.clip_ignore == p_ignore) { + return; + } + st.clip_ignore = p_ignore; + if (st.is_base_state()) { + canvas_item->presort_state = -1; + } else { + canvas_item->presort_states.push_back(st); + canvas_item->presort_state = canvas_item->presort_states.size() - 1; + } + return; + } Item::CommandClipIgnore *ci = canvas_item->alloc_command(); ERR_FAIL_NULL(ci); ci->ignore = p_ignore; + canvas_item->presort_prev_state.clip_ignore = p_ignore; } void RendererCanvasCull::canvas_item_add_animation_slice(RID p_item, double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); + if (canvas_item->presort_level >= 0) { + Item::PresortState st; + if (canvas_item->presort_state >= 0) { + st = canvas_item->presort_states[canvas_item->presort_state]; + } + if (st.animation_length == p_animation_length && st.slice_begin == p_slice_begin && st.slice_end == p_slice_end && st.offset == p_offset) { + return; + } + st.animation_length = p_animation_length; + st.slice_begin = p_slice_begin; + st.slice_end = p_slice_end; + st.offset = p_offset; + if (st.is_base_state()) { + canvas_item->presort_state = -1; + } else { + canvas_item->presort_states.push_back(st); + canvas_item->presort_state = canvas_item->presort_states.size() - 1; + } + return; + } Item::CommandAnimationSlice *as = canvas_item->alloc_command(); ERR_FAIL_NULL(as); @@ -1869,6 +2122,11 @@ void RendererCanvasCull::canvas_item_add_animation_slice(RID p_item, double p_an as->slice_begin = p_slice_begin; as->slice_end = p_slice_end; as->offset = p_offset; + + canvas_item->presort_prev_state.animation_length = p_animation_length; + canvas_item->presort_prev_state.slice_begin = p_slice_begin; + canvas_item->presort_prev_state.slice_end = p_slice_end; + canvas_item->presort_prev_state.offset = p_offset; } void RendererCanvasCull::canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) { @@ -1904,7 +2162,7 @@ void RendererCanvasCull::canvas_item_attach_skeleton(RID p_item, RID p_skeleton) } canvas_item->skeleton = p_skeleton; - Item::Command *c = canvas_item->commands; + Item::Command *c = canvas_item->first_command; while (c) { if (c->type == Item::Command::TYPE_MESH) { @@ -1946,6 +2204,12 @@ void RendererCanvasCull::canvas_item_clear(RID p_item) { Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); + canvas_item->presort_commands.clear(); + canvas_item->presort_states.clear(); + canvas_item->presort_prev_command = nullptr; + canvas_item->presort_level = -1; + canvas_item->presort_state = -1; + canvas_item->presort_prev_state = Item::PresortState(); canvas_item->clear(); #ifdef DEBUG_ENABLED diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index de9b929060..8037ad90e9 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -63,6 +63,95 @@ public: Vector child_items; + struct PresortData { + Item::Command *command = nullptr; + int32_t level = 0; + int32_t state = -1; + }; + struct PresortDataCompare { + _FORCE_INLINE_ bool operator()(const PresortData &l, const PresortData &r) const { + if (l.level == r.level) { + uint64_t l_rid = 0; + uint64_t r_rid = 0; + switch (l.command->type) { + case Item::Command::Type::TYPE_RECT: { + l_rid = static_cast(l.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_NINEPATCH: { + l_rid = static_cast(l.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_POLYGON: { + l_rid = static_cast(l.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_PRIMITIVE: { + l_rid = static_cast(l.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_MESH: { + l_rid = static_cast(l.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_MULTIMESH: { + l_rid = static_cast(l.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_PARTICLES: { + l_rid = static_cast(l.command)->texture.get_id(); + } break; + default: { + l_rid = 0; + } break; + } + switch (r.command->type) { + case Item::Command::Type::TYPE_RECT: { + r_rid = static_cast(r.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_NINEPATCH: { + r_rid = static_cast(r.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_POLYGON: { + r_rid = static_cast(r.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_PRIMITIVE: { + r_rid = static_cast(r.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_MESH: { + r_rid = static_cast(r.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_MULTIMESH: { + r_rid = static_cast(r.command)->texture.get_id(); + } break; + case Item::Command::Type::TYPE_PARTICLES: { + r_rid = static_cast(r.command)->texture.get_id(); + } break; + default: { + r_rid = 0; + } break; + } + return l_rid < r_rid; + } else { + return l.level < r.level; + } + } + }; + + struct PresortState { + Transform2D transform; + bool clip_ignore = false; + double animation_length = 0.0; + double slice_begin = 0.0; + double slice_end = 0.0; + double offset = 0.0; + + _FORCE_INLINE_ bool is_base_state() const { + return transform == Transform2D() && !clip_ignore && animation_length == 0.0 && slice_begin == 0.0 && slice_end == 0.0 && offset == 0.0; + } + }; + + int32_t presort_level = -1; + int32_t presort_state = -1; + Item::Command *presort_prev_command = nullptr; + Item::PresortState presort_prev_state; + LocalVector presort_commands; + LocalVector presort_states; + struct VisibilityNotifierData { Rect2 area; Callable enter_callable; @@ -105,6 +194,16 @@ public: } }; + _FORCE_INLINE_ void _insert_command(Item *p_canvas_item, Item::Command **r_prev_command, Item::Command *p_command) const { + if (*r_prev_command) { + (*r_prev_command)->next = p_command; + } else { + p_canvas_item->first_command = p_command; + } + *r_prev_command = p_command; + } + void _update_presort_state(Item *p_canvas_item, int32_t p_old_state, int32_t p_new_state, Item::Command **r_prev_command); + void _item_queue_update(Item *p_item, bool p_update_dependencies); SelfList::List _item_update_list; @@ -280,6 +379,9 @@ public: void canvas_item_add_clip_ignore(RID p_item, bool p_ignore); void canvas_item_add_animation_slice(RID p_item, double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset); + void canvas_item_set_presort_level(RID p_item, int64_t p_order); + void canvas_item_flush_presort(RID p_item); + void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable); void canvas_item_set_z_index(RID p_item, int p_z); void canvas_item_set_z_as_relative_to_parent(RID p_item, bool p_enable); diff --git a/servers/rendering/renderer_canvas_render.cpp b/servers/rendering/renderer_canvas_render.cpp index a6ad263d1d..f968134e42 100644 --- a/servers/rendering/renderer_canvas_render.cpp +++ b/servers/rendering/renderer_canvas_render.cpp @@ -41,7 +41,7 @@ const Rect2 &RendererCanvasRender::Item::get_rect() const { //must update rect - if (commands == nullptr) { + if (first_command == nullptr) { rect = Rect2(); rect_dirty = false; return rect; @@ -51,7 +51,7 @@ const Rect2 &RendererCanvasRender::Item::get_rect() const { bool found_xform = false; bool first = true; - const Item::Command *c = commands; + const Item::Command *c = first_command; while (c) { Rect2 r; diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index d452bc3611..735a61519c 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -374,7 +374,8 @@ public: const Rect2 &get_rect() const; - Command *commands = nullptr; + Command *heap_command = nullptr; + Command *first_command = nullptr; Command *last_command = nullptr; Vector blocks; uint32_t current_block; @@ -383,15 +384,16 @@ public: #endif template - T *alloc_command() { + T *alloc_command(bool p_set_next = true) { T *command = nullptr; - if (commands == nullptr) { + if (heap_command == nullptr) { // As the most common use case of canvas items is to // use only one command, the first is done with it's // own allocation. The rest of them use blocks. command = memnew(T); command->next = nullptr; - commands = command; + heap_command = command; + first_command = command; last_command = command; } else { //Subsequent commands go into a block. @@ -418,7 +420,9 @@ public: void *memory = c->memory + c->usage; command = memnew_placement(memory, T); command->next = nullptr; - last_command->next = command; + if (p_set_next) { + last_command->next = command; + } last_command = command; c->usage += sizeof(T); break; @@ -430,14 +434,14 @@ public: } void clear() { - // The first one is always allocated on heap + // The `heap_command` is always allocated on heap // the rest go in the blocks - Command *c = commands; + Command *c = first_command; while (c) { Command *n = c->next; - if (c == commands) { - memdelete(commands); - commands = nullptr; + if (c == heap_command) { + memdelete(heap_command); + heap_command = nullptr; } else { c->~Command(); } @@ -451,8 +455,9 @@ public: } } + heap_command = nullptr; last_command = nullptr; - commands = nullptr; + first_command = nullptr; current_block = 0; clip = false; rect_dirty = true; @@ -465,8 +470,9 @@ public: RSE::CanvasItemTextureRepeat texture_repeat; Item() { - commands = nullptr; + heap_command = nullptr; last_command = nullptr; + first_command = nullptr; current_block = 0; light_mask = 1; vp_render = nullptr; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index fad67ae1a3..095cdaa158 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -795,7 +795,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p } if (ci->skeleton.is_valid()) { - const Item::Command *c = ci->commands; + const Item::Command *c = ci->first_command; while (c) { if (c->type == Item::Command::TYPE_MESH) { @@ -2397,7 +2397,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar r_current_batch->use_lighting = use_lighting; } - const Item::Command *c = p_item->commands; + const Item::Command *c = p_item->first_command; while (c) { if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { c = c->next; diff --git a/servers/rendering/rendering_server.cpp b/servers/rendering/rendering_server.cpp index 84b634f39b..b106a0a23b 100644 --- a/servers/rendering/rendering_server.cpp +++ b/servers/rendering/rendering_server.cpp @@ -3365,6 +3365,9 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_item_set_copy_to_backbuffer", "item", "enabled", "rect"), &RenderingServer::canvas_item_set_copy_to_backbuffer); ClassDB::bind_method(D_METHOD("canvas_item_attach_skeleton", "item", "skeleton"), &RenderingServer::canvas_item_attach_skeleton); + ClassDB::bind_method(D_METHOD("canvas_item_set_presort_level", "item", "order"), &RenderingServer::canvas_item_set_presort_level); + ClassDB::bind_method(D_METHOD("canvas_item_flush_presort", "item"), &RenderingServer::canvas_item_flush_presort); + ClassDB::bind_method(D_METHOD("canvas_item_clear", "item"), &RenderingServer::canvas_item_clear); ClassDB::bind_method(D_METHOD("canvas_item_set_draw_index", "item", "index"), &RenderingServer::canvas_item_set_draw_index); ClassDB::bind_method(D_METHOD("canvas_item_set_material", "item", "material"), &RenderingServer::canvas_item_set_material); diff --git a/servers/rendering/rendering_server.h b/servers/rendering/rendering_server.h index 78f63933c9..fff5040d2d 100644 --- a/servers/rendering/rendering_server.h +++ b/servers/rendering/rendering_server.h @@ -844,6 +844,9 @@ public: virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0; virtual void canvas_item_add_animation_slice(RID p_item, double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset) = 0; + virtual void canvas_item_set_presort_level(RID p_item, int64_t p_order) = 0; + virtual void canvas_item_flush_presort(RID p_item) = 0; + virtual void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) = 0; virtual void canvas_item_set_z_index(RID p_item, int p_z) = 0; virtual void canvas_item_set_z_as_relative_to_parent(RID p_item, bool p_enable) = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index e91b58e748..a6f0c56d0e 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -1050,6 +1050,9 @@ public: FUNC2(canvas_item_add_clip_ignore, RID, bool) FUNC5(canvas_item_add_animation_slice, RID, double, double, double, double) + FUNC2(canvas_item_set_presort_level, RID, int64_t); + FUNC1(canvas_item_flush_presort, RID); + FUNC2(canvas_item_set_sort_children_by_y, RID, bool) FUNC2(canvas_item_set_z_index, RID, int) FUNC2(canvas_item_set_z_as_relative_to_parent, RID, bool)