diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 237cd535e74..78c004ea023 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -35,7 +35,11 @@ void CanvasLayer::set_layer(int p_xform) { layer = p_xform; if (viewport.is_valid()) { - VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); + // For the sublayer, we will use the order in which the layer occurs in the scene tree + // rather than just the child_id via get_position_in_parent(). + // We have 32 bits to play with for the sublayer (or more likely 31, as sublayer is signed) + // (see Viewport::CanvasKey constructor in visual_server_viewport.h). + VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, _layer_order_in_tree); vp->_gui_set_root_order_dirty(); } } @@ -59,6 +63,34 @@ void CanvasLayer::set_visible(bool p_visible) { } } +// Make sure layer orders are up to date whenever moving layers in the tree. +void CanvasLayer::_update_layer_orders() { + if (is_inside_tree() && get_tree()) { + Node *root = get_tree()->get_root(); + if (root) { + uint32_t layer_order_count = 0; + _calculate_layer_orders_in_tree(root, layer_order_count); + } + } +} + +// When rendering layers, if the layer id (set by user) and sublayer (child id) is the same +// we need something else sensible which we can sort repeatably to determine which layers should render first, +// so we will simply calculate the order that the layers occur throughout the scene tree. +void CanvasLayer::_calculate_layer_orders_in_tree(Node *p_node, uint32_t &r_order) { + CanvasLayer *layer = Object::cast_to(p_node); + if (layer) { + layer->_layer_order_in_tree = r_order++; + + // Force an update of the layer order in the VisualServer + set_layer(get_layer()); + } + + for (int n = 0; n < p_node->get_child_count(); n++) { + _calculate_layer_orders_in_tree(p_node->get_child(n), r_order); + } +} + void CanvasLayer::show() { set_visible(true); } @@ -183,25 +215,24 @@ void CanvasLayer::_notification(int p_what) { viewport = vp->get_viewport_rid(); VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas); - VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); _update_follow_viewport(); - + _update_layer_orders(); } break; case NOTIFICATION_EXIT_TREE: { ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized."); + _update_layer_orders(); vp->_canvas_layer_remove(this); VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas); viewport = RID(); _update_follow_viewport(false); - } break; case NOTIFICATION_MOVED_IN_PARENT: { - if (is_inside_tree()) { - VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); - } - + // Note: As this step requires traversing the entire scene tree, it is thus expensive + // to move the canvas layer multiple times. Take special care when deleting / moving + // multiple nodes to prevent multiple NOTIFICATION_MOVED_IN_PARENT occurring. + _update_layer_orders(); } break; } } @@ -248,8 +279,8 @@ void CanvasLayer::set_custom_viewport(Node *p_viewport) { viewport = vp->get_viewport_rid(); VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas); - VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); + _update_layer_orders(); } } @@ -379,6 +410,7 @@ CanvasLayer::CanvasLayer() { visible = true; follow_viewport = false; follow_viewport_scale = 1.0; + _layer_order_in_tree = 0; } CanvasLayer::~CanvasLayer() { diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 90bdcd7a78b..47ecfebdc91 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -45,6 +45,7 @@ class CanvasLayer : public Node { int layer; Transform2D transform; RID canvas; + uint32_t _layer_order_in_tree; ObjectID custom_viewport_id; // to check validity Viewport *custom_viewport; @@ -61,6 +62,8 @@ class CanvasLayer : public Node { void _update_xform(); void _update_locrotscale(); void _update_follow_viewport(bool p_force_exit = false); + void _calculate_layer_orders_in_tree(Node *p_node, uint32_t &r_order); + void _update_layer_orders(); protected: void _notification(int p_what);