Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Canvas item hierarchical culling #68738

Merged
merged 1 commit into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,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
1 change: 1 addition & 0 deletions drivers/dummy/rasterizer_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ class RasterizerStorageDummy : public RasterizerStorage {
void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) {}
Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { return Transform2D(); }
uint32_t skeleton_get_revision(RID p_skeleton) const { return 0; }
void skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach) {}

/* Light API */

Expand Down
33 changes: 33 additions & 0 deletions drivers/gles2/rasterizer_storage_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#include "rasterizer_canvas_gles2.h"
#include "rasterizer_scene_gles2.h"
#include "servers/visual/shader_language.h"
#include "servers/visual/visual_server_canvas.h"
#include "servers/visual/visual_server_globals.h"

GLuint RasterizerStorageGLES2::system_fbo = 0;

Expand Down Expand Up @@ -3772,6 +3774,20 @@ void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, cons
skeleton->base_transform_2d = p_base_transform;
}

void RasterizerStorageGLES2::skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach) {
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
ERR_FAIL_NULL(skeleton);
ERR_FAIL_COND(!p_canvas_item.is_valid());

if (p_attach) {
skeleton->linked_canvas_items.push_back(p_canvas_item);
} else {
int64_t found = skeleton->linked_canvas_items.find(p_canvas_item);
ERR_FAIL_COND(found == -1);
skeleton->linked_canvas_items.remove_unordered(found);
}
}

uint32_t RasterizerStorageGLES2::skeleton_get_revision(RID p_skeleton) const {
const Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
ERR_FAIL_COND_V(!skeleton, 0);
Expand Down Expand Up @@ -4101,6 +4117,23 @@ void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<
}

void RasterizerStorageGLES2::update_dirty_skeletons() {
// 2D Skeletons always need to update the polygons so they
// know the bounds have changed.
// TODO : Could we have a separate list for 2D only?
SelfList<Skeleton> *ele = skeleton_update_list.first();

while (ele) {
Skeleton *skeleton = ele->self();

int num_linked = skeleton->linked_canvas_items.size();
for (int n = 0; n < num_linked; n++) {
const RID &rid = skeleton->linked_canvas_items[n];
VSG::canvas->_canvas_item_skeleton_moved(rid);
}

ele = ele->next();
}

if (config.use_skeleton_software) {
return;
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/gles2/rasterizer_storage_gles2.h
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {
Set<RasterizerScene::InstanceBase *> instances;

Transform2D base_transform_2d;
LocalVector<RID> linked_canvas_items;

Skeleton() :
use_2d(false),
Expand All @@ -928,6 +929,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
virtual uint32_t skeleton_get_revision(RID p_skeleton) const;
virtual void skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach);

void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size);

Expand Down
34 changes: 34 additions & 0 deletions drivers/gles3/rasterizer_storage_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include "main/main.h"
#include "rasterizer_canvas_gles3.h"
#include "rasterizer_scene_gles3.h"
#include "servers/visual/visual_server_canvas.h"
#include "servers/visual/visual_server_globals.h"
#include "servers/visual_server.h"

#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED)
Expand Down Expand Up @@ -5314,13 +5316,45 @@ void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, cons
skeleton->base_transform_2d = p_base_transform;
}

void RasterizerStorageGLES3::skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach) {
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
ERR_FAIL_NULL(skeleton);
ERR_FAIL_COND(!p_canvas_item.is_valid());

if (p_attach) {
skeleton->linked_canvas_items.push_back(p_canvas_item);
} else {
int64_t found = skeleton->linked_canvas_items.find(p_canvas_item);
ERR_FAIL_COND(found == -1);
skeleton->linked_canvas_items.remove_unordered(found);
}
}

uint32_t RasterizerStorageGLES3::skeleton_get_revision(RID p_skeleton) const {
const Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
ERR_FAIL_COND_V(!skeleton, 0);
return skeleton->revision;
}

void RasterizerStorageGLES3::update_dirty_skeletons() {
// 2D Skeletons always need to update the polygons so they
// know the bounds have changed.
// TODO : Could we have a separate list for 2D only?
SelfList<Skeleton> *ele = skeleton_update_list.first();

while (ele) {
Skeleton *skeleton = ele->self();

int num_linked = skeleton->linked_canvas_items.size();
for (int n = 0; n < num_linked; n++) {
const RID &rid = skeleton->linked_canvas_items[n];
VSG::canvas->_canvas_item_skeleton_moved(rid);
}

ele = ele->next();
}

// TODO : Is this update necessary for 2D software skinning?
glActiveTexture(GL_TEXTURE0);

while (skeleton_update_list.first()) {
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/rasterizer_storage_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,9 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
GLuint texture;
SelfList<Skeleton> update_list;
Set<RasterizerScene::InstanceBase *> instances; //instances using skeleton

Transform2D base_transform_2d;
LocalVector<RID> linked_canvas_items;

Skeleton() :
use_2d(false),
Expand All @@ -952,6 +954,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
virtual uint32_t skeleton_get_revision(RID p_skeleton) const;
virtual void skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach);

/* Light API */

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 @@ -613,6 +614,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
17 changes: 17 additions & 0 deletions scene/2d/polygon_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ void Polygon2D::_skeleton_bone_setup_changed() {

void Polygon2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
// Must re-establish any existing links with skeletons on re-entering the tree.
update();
} break;
case NOTIFICATION_EXIT_TREE: {
// Always detach skeleton when exiting the tree, so skeletons don't inform
// Polygon2Ds outside the tree that they have moved (this would be useless work).
VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
} break;
case NOTIFICATION_DRAW: {
if (polygon.size() < 3) {
return;
Expand Down Expand Up @@ -665,3 +674,11 @@ Polygon2D::Polygon2D() {
internal_vertices = 0;
current_skeleton_id = 0;
}

Polygon2D::~Polygon2D() {
// Most definitely don't want to leave references to this deleted canvas item
// in the skeleton.
if (get_canvas_item().is_valid()) {
VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
}
}
1 change: 1 addition & 0 deletions scene/2d/polygon_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class Polygon2D : public Node2D {
NodePath get_skeleton() const;

Polygon2D();
virtual ~Polygon2D();
};

#endif // POLYGON_2D_H
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
7 changes: 7 additions & 0 deletions servers/visual/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ class RasterizerStorage {
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
virtual uint32_t skeleton_get_revision(RID p_skeleton) const = 0;
virtual void skeleton_attach_canvas_item(RID p_skeleton, RID p_canvas_item, bool p_attach) = 0;

/* Light API */

Expand Down Expand Up @@ -985,6 +986,7 @@ class RasterizerCanvas {
bool light_masked : 1;
mutable bool custom_rect : 1;
mutable bool rect_dirty : 1;
mutable bool bound_dirty : 1;

Vector<Command *> commands;
mutable Rect2 rect;
Expand Down Expand Up @@ -1024,6 +1026,10 @@ class RasterizerCanvas {
void precalculate_polygon_bone_bounds(const Item::CommandPolygon &p_polygon) const;

public:
// 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 @@ -1201,6 +1207,7 @@ class RasterizerCanvas {
final_modulate = Color(1, 1, 1, 1);
visible = true;
rect_dirty = true;
bound_dirty = true;
custom_rect = false;
behind = false;
material_owner = nullptr;
Expand Down
Loading