|
32 | 32 |
|
33 | 33 | #include "scene/resources/atlas_texture.h" |
34 | 34 |
|
| 35 | +static bool instancing_enabled = false; |
| 36 | +static HashMap<SpriteMeshKey, SpriteBase3D *, SpriteMeshHasher> shared_sprites; |
| 37 | + |
35 | 38 | Color SpriteBase3D::_get_color_accum() { |
36 | 39 | if (!color_dirty) { |
37 | 40 | return color_accum; |
@@ -64,6 +67,64 @@ void SpriteBase3D::_propagate_color_changed() { |
64 | 67 | } |
65 | 68 | } |
66 | 69 |
|
| 70 | +void SpriteBase3D::_start_sharing_sprite() { |
| 71 | + shared_sprites.insert(last_sprite_mesh_key, this); |
| 72 | + sharing_own_mesh = true; |
| 73 | +} |
| 74 | + |
| 75 | +void SpriteBase3D::_stop_sharing_sprite() { |
| 76 | + shared_sprites.erase(last_sprite_mesh_key); |
| 77 | + sharing_own_mesh = false; |
| 78 | + if (!users.is_empty()) { |
| 79 | + // Select a successor and make every other user use that successor's mesh instead. |
| 80 | + SpriteBase3D *successor = nullptr; |
| 81 | + int i = 0; |
| 82 | + for (; i < users.size(); i++) { |
| 83 | + // There may be sprites that have changed the sprite they're using earlier this frame, so we have to filter them out. |
| 84 | + if (users[i] && users[i]->using_sprite == this) { |
| 85 | + successor = users[i]; |
| 86 | + successor->_start_sharing_sprite(); |
| 87 | + // 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. |
| 88 | + // memcpy is used directly because buffer sizes are guaranteed to be identical across all SpriteBase3Ds. |
| 89 | + memcpy(successor->vertex_buffer.ptrw(), vertex_buffer.ptr(), vertex_buffer.size()); |
| 90 | + memcpy(successor->attribute_buffer.ptrw(), attribute_buffer.ptr(), attribute_buffer.size()); |
| 91 | + RS::get_singleton()->mesh_surface_update_vertex_region(successor->mesh, 0, 0, successor->vertex_buffer); |
| 92 | + RS::get_singleton()->mesh_surface_update_attribute_region(successor->mesh, 0, 0, successor->attribute_buffer); |
| 93 | + RS::get_singleton()->mesh_set_custom_aabb(successor->mesh, aabb); |
| 94 | + successor->using_sprite = nullptr; |
| 95 | + successor->set_base(successor->mesh); |
| 96 | + successor->set_aabb(aabb); |
| 97 | + i++; // Skip the successor for the next for-loop. |
| 98 | + break; |
| 99 | + } |
| 100 | + } |
| 101 | + // Propagate the change to remaining users that haven't changed their using_sprite. |
| 102 | + // Note: This works even if the same user is registered twice. Very rare but can still happen. |
| 103 | + for (; i < users.size(); i++) { |
| 104 | + if (users[i] && users[i]->using_sprite == this) { |
| 105 | + users[i]->_start_using_sprite(successor); |
| 106 | + } |
| 107 | + } |
| 108 | + // Now every user has moved on, we can clear the users list. |
| 109 | + users.clear(); |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +void SpriteBase3D::_start_using_sprite(SpriteBase3D *p_using_sprite) { |
| 114 | + using_sprite = p_using_sprite; |
| 115 | + using_sprite_user_index = using_sprite->users.size(); |
| 116 | + set_base(using_sprite->mesh); |
| 117 | + set_aabb(using_sprite->aabb); |
| 118 | + using_sprite->users.push_back(this); |
| 119 | + // We don't need to remove this sprite from the previous shared sprite's users list, |
| 120 | + // as setting using_sprite means they will be detected and filtered out later. |
| 121 | +} |
| 122 | + |
| 123 | +void SpriteBase3D::_stop_using_sprite() { |
| 124 | + using_sprite->users.ptrw()[using_sprite_user_index] = nullptr; |
| 125 | + using_sprite = nullptr; |
| 126 | +} |
| 127 | + |
67 | 128 | void SpriteBase3D::_notification(int p_what) { |
68 | 129 | switch (p_what) { |
69 | 130 | case NOTIFICATION_ENTER_TREE: { |
@@ -221,6 +282,87 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, |
221 | 282 | uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) |
222 | 283 | }; |
223 | 284 |
|
| 285 | + BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_DISABLED; |
| 286 | + if (get_draw_flag(FLAG_TRANSPARENT)) { |
| 287 | + if (get_alpha_cut_mode() == ALPHA_CUT_DISCARD) { |
| 288 | + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_SCISSOR; |
| 289 | + } else if (get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS) { |
| 290 | + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS; |
| 291 | + } else if (get_alpha_cut_mode() == ALPHA_CUT_HASH) { |
| 292 | + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_HASH; |
| 293 | + } else { |
| 294 | + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA; |
| 295 | + } |
| 296 | + } |
| 297 | + |
| 298 | + RID shader_rid; |
| 299 | + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), alpha_antialiasing_mode, &shader_rid); |
| 300 | + |
| 301 | + if (last_shader != shader_rid) { |
| 302 | + RS::get_singleton()->material_set_shader(get_material(), shader_rid); |
| 303 | + last_shader = shader_rid; |
| 304 | + } |
| 305 | + if (last_texture != p_texture->get_rid()) { |
| 306 | + RS::get_singleton()->material_set_param(get_material(), "texture_albedo", p_texture->get_rid()); |
| 307 | + RS::get_singleton()->material_set_param(get_material(), "albedo_texture_size", Vector2i(p_texture->get_width(), p_texture->get_height())); |
| 308 | + last_texture = p_texture->get_rid(); |
| 309 | + } |
| 310 | + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { |
| 311 | + RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); |
| 312 | + RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); |
| 313 | + } |
| 314 | + |
| 315 | + RS::get_singleton()->material_set_param(get_material(), "alpha_scissor_threshold", alpha_scissor_threshold); |
| 316 | + RS::get_singleton()->material_set_param(get_material(), "alpha_hash_scale", alpha_hash_scale); |
| 317 | + RS::get_singleton()->material_set_param(get_material(), "alpha_antialiasing_edge", alpha_antialiasing_edge); |
| 318 | + |
| 319 | + if (instancing_enabled) { |
| 320 | + SpriteMeshKey sprite_mesh_key; |
| 321 | + sprite_mesh_key.final_rect = final_rect; |
| 322 | + sprite_mesh_key.final_src_rect = final_src_rect; |
| 323 | + sprite_mesh_key.v_color = *(uint32_t *)v_color; |
| 324 | + sprite_mesh_key.v_normal = v_normal; |
| 325 | + sprite_mesh_key.shader = last_shader; |
| 326 | + sprite_mesh_key.texture = last_texture; |
| 327 | + sprite_mesh_key.px_size = px_size; |
| 328 | + sprite_mesh_key.render_priority = render_priority; |
| 329 | + sprite_mesh_key.axis = axis; |
| 330 | + sprite_mesh_key.hflip = hflip; |
| 331 | + sprite_mesh_key.vflip = vflip; |
| 332 | + sprite_mesh_key.alpha_cut_disabled = get_alpha_cut_mode() == ALPHA_CUT_DISABLED; |
| 333 | + sprite_mesh_key.alpha_antialiasing_edge = alpha_antialiasing_edge; |
| 334 | + sprite_mesh_key.alpha_hash_scale = alpha_hash_scale; |
| 335 | + sprite_mesh_key.alpha_scissor_threshold = alpha_scissor_threshold; |
| 336 | + if (sharing_own_mesh) { |
| 337 | + if (last_sprite_mesh_key != sprite_mesh_key) { |
| 338 | + // Sprite mesh data changed. |
| 339 | + _stop_sharing_sprite(); |
| 340 | + } else { |
| 341 | + // Sprite mesh data unchanged. |
| 342 | + return; |
| 343 | + } |
| 344 | + } |
| 345 | + // Try to see if there's any sprite whose mesh can be used instead. |
| 346 | + SpriteBase3D **sprite_ptr = shared_sprites.getptr(sprite_mesh_key); |
| 347 | + last_sprite_mesh_key = sprite_mesh_key; |
| 348 | + if (sprite_ptr) { |
| 349 | + if (*sprite_ptr != using_sprite) { |
| 350 | + // Found new sprite that can be reused. |
| 351 | + _start_using_sprite(*sprite_ptr); |
| 352 | + return; |
| 353 | + } else { |
| 354 | + // Keep using same sprite. |
| 355 | + return; |
| 356 | + } |
| 357 | + } |
| 358 | + // Otherwise, setup mesh data and register this sprite's mesh for sharing. |
| 359 | + _start_sharing_sprite(); |
| 360 | + if (using_sprite) { |
| 361 | + // This sprite may have been sharing another sprite's mesh before, so we need to stop that here. |
| 362 | + _stop_using_sprite(); |
| 363 | + } |
| 364 | + } |
| 365 | + |
224 | 366 | for (int i = 0; i < 4; i++) { |
225 | 367 | Vector3 vtx; |
226 | 368 | vtx[x_axis] = vertices[i][0]; |
@@ -264,46 +406,13 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, |
264 | 406 | break; |
265 | 407 | } |
266 | 408 |
|
267 | | - RID mesh_new = get_mesh(); |
| 409 | + RID mesh_new = mesh; |
| 410 | + set_base(mesh_new); |
268 | 411 | RS::get_singleton()->mesh_surface_update_vertex_region(mesh_new, 0, 0, vertex_buffer); |
269 | 412 | RS::get_singleton()->mesh_surface_update_attribute_region(mesh_new, 0, 0, attribute_buffer); |
270 | 413 |
|
271 | 414 | RS::get_singleton()->mesh_set_custom_aabb(mesh_new, aabb_new); |
272 | 415 | set_aabb(aabb_new); |
273 | | - |
274 | | - RS::get_singleton()->material_set_param(get_material(), "alpha_scissor_threshold", alpha_scissor_threshold); |
275 | | - RS::get_singleton()->material_set_param(get_material(), "alpha_hash_scale", alpha_hash_scale); |
276 | | - RS::get_singleton()->material_set_param(get_material(), "alpha_antialiasing_edge", alpha_antialiasing_edge); |
277 | | - |
278 | | - BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_DISABLED; |
279 | | - if (get_draw_flag(FLAG_TRANSPARENT)) { |
280 | | - if (get_alpha_cut_mode() == ALPHA_CUT_DISCARD) { |
281 | | - mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_SCISSOR; |
282 | | - } else if (get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS) { |
283 | | - mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS; |
284 | | - } else if (get_alpha_cut_mode() == ALPHA_CUT_HASH) { |
285 | | - mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_HASH; |
286 | | - } else { |
287 | | - mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA; |
288 | | - } |
289 | | - } |
290 | | - |
291 | | - RID shader_rid; |
292 | | - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), alpha_antialiasing_mode, &shader_rid); |
293 | | - |
294 | | - if (last_shader != shader_rid) { |
295 | | - RS::get_singleton()->material_set_shader(get_material(), shader_rid); |
296 | | - last_shader = shader_rid; |
297 | | - } |
298 | | - if (last_texture != p_texture->get_rid()) { |
299 | | - RS::get_singleton()->material_set_param(get_material(), "texture_albedo", p_texture->get_rid()); |
300 | | - RS::get_singleton()->material_set_param(get_material(), "albedo_texture_size", Vector2i(p_texture->get_width(), p_texture->get_height())); |
301 | | - last_texture = p_texture->get_rid(); |
302 | | - } |
303 | | - if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { |
304 | | - RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); |
305 | | - RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); |
306 | | - } |
307 | 416 | } |
308 | 417 |
|
309 | 418 | void SpriteBase3D::set_centered(bool p_center) { |
@@ -697,6 +806,13 @@ void SpriteBase3D::_bind_methods() { |
697 | 806 | } |
698 | 807 |
|
699 | 808 | SpriteBase3D::SpriteBase3D() { |
| 809 | + static bool static_initialized = false; |
| 810 | + if (!static_initialized) { |
| 811 | + static_initialized = true; |
| 812 | + // Auto-instancing isn't supported in the compatibility renderer. |
| 813 | + instancing_enabled = RS::get_singleton()->get_current_rendering_method() != "gl_compatibility"; |
| 814 | + } |
| 815 | + |
700 | 816 | for (int i = 0; i < FLAG_MAX; i++) { |
701 | 817 | flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED; |
702 | 818 | } |
@@ -774,6 +890,14 @@ SpriteBase3D::~SpriteBase3D() { |
774 | 890 | ERR_FAIL_NULL(RenderingServer::get_singleton()); |
775 | 891 | RenderingServer::get_singleton()->free(mesh); |
776 | 892 | RenderingServer::get_singleton()->free(material); |
| 893 | + |
| 894 | + if (instancing_enabled && sharing_own_mesh) { |
| 895 | + _stop_sharing_sprite(); |
| 896 | + } |
| 897 | + |
| 898 | + if (using_sprite) { |
| 899 | + _stop_using_sprite(); |
| 900 | + } |
777 | 901 | } |
778 | 902 |
|
779 | 903 | /////////////////////////////////////////// |
|
0 commit comments