Skip to content

Commit

Permalink
Canvas item hierarchical culling
Browse files Browse the repository at this point in the history
Adds optional hierarchical culling to the 2D rendering (within VisualServer).

Each canvas item maintains a bound in local space of the item itself and all child / grandchild items. This allows branches to be culled at once when they don't intersect a viewport.

Also caches global transforms and rects.
  • Loading branch information
lawnjelly committed Nov 16, 2022
1 parent 92aedd5 commit 2188002
Show file tree
Hide file tree
Showing 12 changed files with 861 additions and 12 deletions.
4 changes: 4 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,10 @@
[b]Experimental.[/b] If set to on, uses the [code]GL_STREAM_DRAW[/code] flag for legacy buffer uploads. If off, uses the [code]GL_DYNAMIC_DRAW[/code] flag.
[b]Note:[/b] Use with care. You are advised to leave this as default for exports. A non-default setting that works better on your machine may adversely affect performance for end users.
</member>
<member name="rendering/2d/options/culling_mode" type="int" setter="" getter="" default="1">
The culling mode determines the method used for rejecting canvas items that are outside a viewport. The visual result should be identical, but some modes may be faster for a particular project.
You can either cull items individually ([code]Item mode[/code]), or use hierarchical culling ([code]Node mode[/code]) which has a little more housekeeping but can increase performance by culling large numbers of items at once.
</member>
<member name="rendering/2d/options/ninepatch_mode" type="int" setter="" getter="" default="1">
Choose between fixed mode where corner scalings are preserved matching the artwork, and scaling mode.
Not available in GLES3 when [member rendering/batching/options/use_batching] is off.
Expand Down
12 changes: 12 additions & 0 deletions scene/2d/canvas_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "scene/resources/style_box.h"
#include "scene/resources/texture.h"
#include "scene/scene_string_names.h"
#include "servers/visual/visual_server_constants.h"
#include "servers/visual/visual_server_raster.h"
#include "servers/visual_server.h"

Expand Down Expand Up @@ -616,6 +617,17 @@ void CanvasItem::_notification(int p_what) {
}
}

#ifdef DEV_ENABLED
void CanvasItem::_name_changed_notify() {
// Even in DEV builds, there is no point in calling this unless we are debugging
// canvas item names. Even calling the stub function will be expensive, as there
// are a lot of canvas items.
#ifdef VISUAL_SERVER_CANVAS_DEBUG_ITEM_NAMES
VisualServer::get_singleton()->canvas_item_set_name(canvas_item, get_name());
#endif
}
#endif

void CanvasItem::update() {
if (!is_inside_tree()) {
return;
Expand Down
4 changes: 4 additions & 0 deletions scene/2d/canvas_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ class CanvasItem : public Node {
void _notification(int p_what);
static void _bind_methods();

#ifdef DEV_ENABLED
virtual void _name_changed_notify();
#endif

public:
enum {
NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, //unique
Expand Down
14 changes: 13 additions & 1 deletion scene/main/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,15 @@ StringName Node::get_name() const {

void Node::_set_name_nocheck(const StringName &p_name) {
data.name = p_name;
#ifdef DEV_ENABLED
_name_changed_notify();
#endif
}

#ifdef DEV_ENABLED
void Node::_name_changed_notify() {
}
#endif

void Node::set_name(const String &p_name) {
String name = p_name.validate_node_name();
Expand All @@ -1078,6 +1086,10 @@ void Node::set_name(const String &p_name) {
get_tree()->node_renamed(this);
get_tree()->tree_changed();
}

#ifdef DEV_ENABLED
_name_changed_notify();
#endif
}

static bool node_hrcr = false;
Expand Down Expand Up @@ -1262,7 +1274,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
//add a child node quickly, without name validation

p_child->data.name = p_name;
p_child->_set_name_nocheck(p_name);
p_child->data.pos = data.children.size();
data.children.push_back(p_child);
p_child->data.parent = this;
Expand Down
3 changes: 3 additions & 0 deletions scene/main/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ class Node : public Object {
virtual void remove_child_notify(Node *p_child);
virtual void move_child_notify(Node *p_child);
virtual void owner_changed_notify();
#ifdef DEV_ENABLED
virtual void _name_changed_notify();
#endif

virtual void _physics_interpolated_changed();

Expand Down
9 changes: 9 additions & 0 deletions servers/visual/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,8 @@ class RasterizerCanvas {
bool light_masked : 1;
mutable bool custom_rect : 1;
mutable bool rect_dirty : 1;
mutable bool xform_dirty : 1;
mutable bool bound_dirty : 1;

Vector<Command *> commands;
mutable Rect2 rect;
Expand Down Expand Up @@ -984,6 +986,10 @@ class RasterizerCanvas {

Rect2 global_rect_cache;

// the rect containing this item and all children,
// in local space.
Rect2 local_bound;

const Rect2 &get_rect() const {
if (custom_rect) {
return rect;
Expand Down Expand Up @@ -1185,6 +1191,7 @@ class RasterizerCanvas {
commands.clear();
clip = false;
rect_dirty = true;
xform_dirty = true;
final_clip_owner = nullptr;
material_owner = nullptr;
light_masked = false;
Expand All @@ -1199,6 +1206,8 @@ class RasterizerCanvas {
final_modulate = Color(1, 1, 1, 1);
visible = true;
rect_dirty = true;
xform_dirty = true;
bound_dirty = true;
custom_rect = false;
behind = false;
material_owner = nullptr;
Expand Down
Loading

0 comments on commit 2188002

Please sign in to comment.