Skip to content

Commit

Permalink
Merge pull request godotengine#68738 from lawnjelly/faster_canvas_item
Browse files Browse the repository at this point in the history
[3.x] Canvas item hierarchical culling
  • Loading branch information
akien-mga authored Jun 27, 2023
2 parents 05e2912 + b777a9e commit 29eeb46
Show file tree
Hide file tree
Showing 19 changed files with 857 additions and 19 deletions.
4 changes: 4 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,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

0 comments on commit 29eeb46

Please sign in to comment.