Skip to content

Commit

Permalink
Merge pull request #80106 from lawnjelly/hier_multimesh_culling
Browse files Browse the repository at this point in the history
[3.x] Fix 2D MultiMesh hierarchical culling
  • Loading branch information
akien-mga committed Aug 8, 2023
2 parents 08832a0 + ad577e3 commit 39ed081
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 5 deletions.
1 change: 1 addition & 0 deletions drivers/dummy/rasterizer_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ class RasterizerStorageDummy : public RasterizerStorage {
AABB _multimesh_get_aabb(RID p_multimesh) const { return AABB(); }

MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const { return nullptr; }
void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) {}

/* IMMEDIATE API */

Expand Down
36 changes: 35 additions & 1 deletion drivers/gles2/rasterizer_storage_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3388,6 +3388,24 @@ RasterizerStorage::MMInterpolator *RasterizerStorageGLES2::_multimesh_get_interp
return &multimesh->interpolator;
}

void RasterizerStorageGLES2::multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) {
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_COND(!p_canvas_item.is_valid());

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

void RasterizerStorageGLES2::update_dirty_multimeshes() {
while (multimesh_update_list.first()) {
MultiMesh *multimesh = multimesh_update_list.first()->self();
Expand Down Expand Up @@ -3457,6 +3475,14 @@ void RasterizerStorageGLES2::update_dirty_multimeshes() {
}

multimesh->aabb = aabb;

// Inform any linked canvas items that bounds have changed
// (for hierarchical culling).
int num_linked = multimesh->linked_canvas_items.size();
for (int n = 0; n < num_linked; n++) {
const RID &rid = multimesh->linked_canvas_items[n];
VSG::canvas->_canvas_item_invalidate_local_bound(rid);
}
}

multimesh->dirty_aabb = false;
Expand Down Expand Up @@ -4128,7 +4154,7 @@ void RasterizerStorageGLES2::update_dirty_skeletons() {
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);
VSG::canvas->_canvas_item_invalidate_local_bound(rid);
}

ele = ele->next();
Expand Down Expand Up @@ -6052,6 +6078,14 @@ bool RasterizerStorageGLES2::free(RID p_rid) {
_interpolation_data.notify_free_multimesh(p_rid);

MultiMesh *multimesh = multimesh_owner.get(p_rid);

// remove any references in linked canvas items
int num_linked = multimesh->linked_canvas_items.size();
for (int n = 0; n < num_linked; n++) {
const RID &rid = multimesh->linked_canvas_items[n];
VSG::canvas->_canvas_item_remove_references(rid, p_rid);
}

multimesh->instance_remove_deps();

if (multimesh->mesh.is_valid()) {
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 @@ -789,6 +789,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {
bool dirty_data;

MMInterpolator interpolator;
LocalVector<RID> linked_canvas_items;

MultiMesh() :
size(0),
Expand Down Expand Up @@ -835,6 +836,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {

virtual AABB _multimesh_get_aabb(RID p_multimesh) const;
virtual MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const;
virtual void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach);

void update_dirty_multimeshes();

Expand Down
38 changes: 36 additions & 2 deletions drivers/gles3/rasterizer_storage_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4923,6 +4923,24 @@ RasterizerStorage::MMInterpolator *RasterizerStorageGLES3::_multimesh_get_interp
return &multimesh->interpolator;
}

void RasterizerStorageGLES3::multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) {
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
ERR_FAIL_NULL(multimesh);
ERR_FAIL_COND(!p_canvas_item.is_valid());

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

void RasterizerStorageGLES3::update_dirty_multimeshes() {
while (multimesh_update_list.first()) {
MultiMesh *multimesh = multimesh_update_list.first()->self();
Expand Down Expand Up @@ -5001,6 +5019,14 @@ void RasterizerStorageGLES3::update_dirty_multimeshes() {
}

multimesh->aabb = aabb;

// Inform any linked canvas items that bounds have changed
// (for hierarchical culling).
int num_linked = multimesh->linked_canvas_items.size();
for (int n = 0; n < num_linked; n++) {
const RID &rid = multimesh->linked_canvas_items[n];
VSG::canvas->_canvas_item_invalidate_local_bound(rid);
}
}
multimesh->dirty_aabb = false;
multimesh->dirty_data = false;
Expand Down Expand Up @@ -5348,7 +5374,7 @@ void RasterizerStorageGLES3::update_dirty_skeletons() {
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);
VSG::canvas->_canvas_item_invalidate_local_bound(rid);
}

ele = ele->next();
Expand Down Expand Up @@ -7865,8 +7891,16 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
// remove from interpolator
_interpolation_data.notify_free_multimesh(p_rid);

// delete the texture
MultiMesh *multimesh = multimesh_owner.get(p_rid);

// remove any references in linked canvas items
int num_linked = multimesh->linked_canvas_items.size();
for (int n = 0; n < num_linked; n++) {
const RID &rid = multimesh->linked_canvas_items[n];
VSG::canvas->_canvas_item_remove_references(rid, p_rid);
}

// delete the texture
multimesh->instance_remove_deps();

if (multimesh->mesh.is_valid()) {
Expand Down
2 changes: 2 additions & 0 deletions drivers/gles3/rasterizer_storage_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
bool dirty_data;

MMInterpolator interpolator;
LocalVector<RID> linked_canvas_items;

MultiMesh() :
size(0),
Expand Down Expand Up @@ -867,6 +868,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {

virtual AABB _multimesh_get_aabb(RID p_multimesh) const;
virtual MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const;
virtual void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach);

/* IMMEDIATE API */

Expand Down
25 changes: 25 additions & 0 deletions servers/visual/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ class RasterizerStorage {
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible);
virtual int multimesh_get_visible_instances(RID p_multimesh) const;
virtual AABB multimesh_get_aabb(RID p_multimesh) const;
virtual void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) = 0;

virtual RID _multimesh_create() = 0;
virtual void _multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE) = 0;
Expand Down Expand Up @@ -817,6 +818,8 @@ class RasterizerCanvas {
TYPE_MULTIRECT,
};

virtual bool contains_reference(const RID &p_rid) const { return false; }

Type type;
virtual ~Command() {}
};
Expand Down Expand Up @@ -946,7 +949,15 @@ class RasterizerCanvas {
RID multimesh;
RID texture;
RID normal_map;
RID canvas_item;
virtual bool contains_reference(const RID &p_rid) const { return multimesh == p_rid; }
CommandMultiMesh() { type = TYPE_MULTIMESH; }
virtual ~CommandMultiMesh() {
// Remove any backlinks from multimesh to canvas item.
if (multimesh.is_valid()) {
RasterizerStorage::base_singleton->multimesh_attach_canvas_item(multimesh, canvas_item, false);
}
}
};

struct CommandParticles : public Command {
Expand Down Expand Up @@ -1199,6 +1210,20 @@ class RasterizerCanvas {
return rect;
}

void remove_references(const RID &p_rid) {
for (int i = commands.size() - 1; i >= 0; i--) {
if (commands[i]->contains_reference(p_rid)) {
memdelete(commands[i]);

// This could possibly be unordered if occurring close
// to canvas_item deletion, but is
// unlikely to make much performance difference,
// and is safer.
commands.remove(i);
}
}
}

void clear() {
for (int i = 0; i < commands.size(); i++) {
memdelete(commands[i]);
Expand Down
18 changes: 17 additions & 1 deletion servers/visual/visual_server_canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1421,10 +1421,17 @@ void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p
mm->multimesh = p_mesh;
mm->texture = p_texture;
mm->normal_map = p_normal_map;
mm->canvas_item = p_item;

canvas_item->rect_dirty = true;
canvas_item->commands.push_back(mm);
_make_bound_dirty(canvas_item);

// Attach to multimesh a backlink to enable updating
// the canvas item local bound when the multimesh changes.
if (p_mesh.is_valid()) {
VSG::storage->multimesh_attach_canvas_item(p_mesh, p_item, true);
}
}

void VisualServerCanvas::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) {
Expand Down Expand Up @@ -1588,7 +1595,16 @@ void VisualServerCanvas::canvas_item_attach_skeleton(RID p_item, RID p_skeleton)
}
}

void VisualServerCanvas::_canvas_item_skeleton_moved(RID p_item) {
// Canvas items may contain references to other resources (such as MultiMesh).
// If the resources are deleted first, and the canvas_item retains references, it
// will crash / error when it tries to access these.
void VisualServerCanvas::_canvas_item_remove_references(RID p_item, RID p_rid) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
canvas_item->remove_references(p_rid);
}

void VisualServerCanvas::_canvas_item_invalidate_local_bound(RID p_item) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
_make_bound_dirty(canvas_item);
Expand Down
4 changes: 3 additions & 1 deletion servers/visual/visual_server_canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ class VisualServerCanvas {
void canvas_item_set_use_parent_material(RID p_item, bool p_enable);

void canvas_item_attach_skeleton(RID p_item, RID p_skeleton);
void _canvas_item_skeleton_moved(RID p_item);
void canvas_item_set_skeleton_relative_xform(RID p_item, Transform2D p_relative_xform);
Rect2 _debug_canvas_item_get_rect(RID p_item);
Rect2 _debug_canvas_item_get_local_bound(RID p_item);
Expand All @@ -265,6 +264,9 @@ class VisualServerCanvas {
void canvas_item_reset_physics_interpolation(RID p_item);
void canvas_item_transform_physics_interpolation(RID p_item, Transform2D p_transform);

void _canvas_item_invalidate_local_bound(RID p_item);
void _canvas_item_remove_references(RID p_item, RID p_rid);

RID canvas_light_create();
void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
void canvas_light_set_enabled(RID p_light, bool p_enabled);
Expand Down

0 comments on commit 39ed081

Please sign in to comment.