3232
3333#include " scene/resources/atlas_texture.h"
3434
35+ static bool instancing_enabled = false ;
36+ static HashMap<SpriteMeshKey, SpriteBase3D *, SpriteMeshHasher> shared_sprites;
37+
3538Color SpriteBase3D::_get_color_accum () {
3639 if (!color_dirty) {
3740 return color_accum;
@@ -64,6 +67,51 @@ void SpriteBase3D::_propagate_color_changed() {
6467 }
6568}
6669
70+ void SpriteBase3D::_stop_sharing_sprite () {
71+ shared_sprites.erase (last_sprite_mesh_key);
72+ sharing_own_mesh = false ;
73+ if (!users.is_empty ()) {
74+ // Select a successor and make every other user use that successor's mesh instead.
75+ SpriteBase3D *successor = nullptr ;
76+ int i = 0 ;
77+ for (; i < users.size (); i++) {
78+ // There may be sprites that have changed the sprite they're using earlier this frame, so we have to filter them out.
79+ if (users[i] && users[i]->using_sprite == this ) {
80+ successor = users[i];
81+ shared_sprites.insert (last_sprite_mesh_key, successor);
82+ // Copy mesh data to successor. Need to store data in vertex and attribute buffer and not just update the RenderingServer mesh directly so they can be copied again later.
83+ // memcpy is used directly because buffer sizes are guaranteed to be identical across all SpriteBase3Ds.
84+ memcpy (successor->vertex_buffer .ptrw (), vertex_buffer.ptr (), vertex_buffer.size ());
85+ memcpy (successor->attribute_buffer .ptrw (), attribute_buffer.ptr (), attribute_buffer.size ());
86+ RS::get_singleton ()->mesh_surface_update_vertex_region (successor->mesh , 0 , 0 , successor->vertex_buffer );
87+ RS::get_singleton ()->mesh_surface_update_attribute_region (successor->mesh , 0 , 0 , successor->attribute_buffer );
88+ RS::get_singleton ()->mesh_set_custom_aabb (successor->mesh , aabb);
89+ if (last_sprite_mesh_key.alpha_cut_disabled ) {
90+ RS::get_singleton ()->material_set_render_priority (get_material (), get_render_priority ());
91+ RS::get_singleton ()->mesh_surface_set_material (successor->mesh , 0 , get_material ());
92+ }
93+ successor->using_sprite = nullptr ;
94+ successor->using_sprite_user_index = -1 ;
95+ successor->set_base (successor->mesh );
96+ i++; // Skip the successor for the next for-loop.
97+ break ;
98+ }
99+ }
100+ // Propagate the change to remaining users that haven't changed their using_sprite.
101+ // Note: This works even if the same user is registered twice. Very rare but can still happen.
102+ for (; i < users.size (); i++) {
103+ if (users[i] && users[i]->using_sprite == this ) {
104+ users[i]->set_base (successor->mesh );
105+ users[i]->using_sprite = successor;
106+ users[i]->using_sprite_user_index = successor->users .size ();
107+ successor->users .push_back (users[i]);
108+ }
109+ }
110+ // Now every user has moved on, we can clear the users list.
111+ users.clear ();
112+ }
113+ }
114+
67115void SpriteBase3D::_notification (int p_what) {
68116 switch (p_what) {
69117 case NOTIFICATION_ENTER_TREE: {
@@ -221,6 +269,44 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect,
221269 uint8_t (CLAMP (color.a * 255.0 , 0.0 , 255.0 ))
222270 };
223271
272+ if (instancing_enabled) {
273+ SpriteMeshKey sprite_mesh_key;
274+ memcpy (&sprite_mesh_key.vertices , vertices, sizeof (Vector2) * 4 );
275+ memcpy (&sprite_mesh_key.uvs , uvs, sizeof (Vector2) * 4 );
276+ sprite_mesh_key.v_color = *(uint32_t *)v_color;
277+ sprite_mesh_key.v_normal = v_normal;
278+ sprite_mesh_key.alpha_cut_disabled = get_alpha_cut_mode () == ALPHA_CUT_DISABLED;
279+ if (sharing_own_mesh) {
280+ if (last_sprite_mesh_key != sprite_mesh_key) {
281+ // Sprite mesh data changed.
282+ _stop_sharing_sprite ();
283+ } else {
284+ // Sprite mesh data unchanged.
285+ return ;
286+ }
287+ }
288+ // Try to see if there's any sprite whose mesh can be used instead.
289+ SpriteBase3D **sprite_ptr = shared_sprites.getptr (sprite_mesh_key);
290+ last_sprite_mesh_key = sprite_mesh_key;
291+ if (sprite_ptr) {
292+ if (*sprite_ptr != using_sprite) {
293+ // Found new sprite that can be reused.
294+ using_sprite = *sprite_ptr;
295+ set_base (using_sprite->mesh );
296+ using_sprite_user_index = using_sprite->users .size ();
297+ using_sprite->users .push_back (this );
298+ // We don't need to remove this sprite from the previous shared sprite's users list, as they will be detected and filtered out later.
299+ return ;
300+ } else {
301+ // Keep using same sprite.
302+ return ;
303+ }
304+ }
305+ // Otherwise, setup mesh data and register this sprite's mesh for sharing.
306+ shared_sprites.insert (sprite_mesh_key, this );
307+ sharing_own_mesh = true ;
308+ }
309+
224310 for (int i = 0 ; i < 4 ; i++) {
225311 Vector3 vtx;
226312 vtx[x_axis] = vertices[i][0 ];
@@ -243,7 +329,8 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect,
243329 memcpy (&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4 );
244330 }
245331
246- RID mesh_new = get_mesh ();
332+ RID mesh_new = mesh;
333+ set_base (mesh_new);
247334 RS::get_singleton ()->mesh_surface_update_vertex_region (mesh_new, 0 , 0 , vertex_buffer);
248335 RS::get_singleton ()->mesh_surface_update_attribute_region (mesh_new, 0 , 0 , attribute_buffer);
249336
@@ -676,6 +763,13 @@ void SpriteBase3D::_bind_methods() {
676763}
677764
678765SpriteBase3D::SpriteBase3D () {
766+ static bool static_initialized = false ;
767+ if (!static_initialized) {
768+ static_initialized = true ;
769+ // Auto-instancing isn't supported in the compatibility renderer.
770+ instancing_enabled = RS::get_singleton ()->get_current_rendering_method () != " gl_compatibility" ;
771+ }
772+
679773 for (int i = 0 ; i < FLAG_MAX; i++) {
680774 flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED;
681775 }
@@ -753,6 +847,14 @@ SpriteBase3D::~SpriteBase3D() {
753847 ERR_FAIL_NULL (RenderingServer::get_singleton ());
754848 RenderingServer::get_singleton ()->free (mesh);
755849 RenderingServer::get_singleton ()->free (material);
850+
851+ if (instancing_enabled && sharing_own_mesh) {
852+ _stop_sharing_sprite ();
853+ }
854+
855+ if (using_sprite) {
856+ using_sprite->users .ptrw ()[using_sprite_user_index] = nullptr ;
857+ }
756858}
757859
758860// /////////////////////////////////////////
0 commit comments