From 305216f55819c3385b3fc3941d0715abd386b74f Mon Sep 17 00:00:00 2001 From: clayjohn Date: Mon, 3 Feb 2025 23:02:20 -0800 Subject: [PATCH] Overhaul the cull mask internals for Lights, Decals, and Particle Colliders Properly pair and unpair instances based on cull mask to avoid any unnecessary processing and to ensure that changing the cull_mask and layer_mask actually updates culling behavior --- drivers/gles3/storage/light_storage.cpp | 2 +- drivers/gles3/storage/particles_storage.cpp | 7 +++ drivers/gles3/storage/particles_storage.h | 1 + .../dummy/storage/particles_storage.h | 1 + .../forward_mobile/scene_forward_mobile.glsl | 3 -- .../renderer_rd/storage_rd/light_storage.cpp | 2 +- .../storage_rd/particles_storage.cpp | 7 +++ .../storage_rd/particles_storage.h | 1 + .../storage_rd/texture_storage.cpp | 2 +- servers/rendering/renderer_scene_cull.cpp | 44 ++++++++++++++++--- servers/rendering/renderer_scene_cull.h | 9 ++-- servers/rendering/storage/particles_storage.h | 1 + servers/rendering/storage/utilities.h | 1 + 13 files changed, 67 insertions(+), 14 deletions(-) diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index b7cd22de9fc..7d2d7722494 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -209,7 +209,7 @@ void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) { light->cull_mask = p_mask; light->version++; - light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_CULL_MASK); } void LightStorage::light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) { diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 599412c5b2d..d3f20880ca3 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -1319,6 +1319,13 @@ void ParticlesStorage::particles_collision_set_cull_mask(RID p_particles_collisi ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_NULL(particles_collision); particles_collision->cull_mask = p_cull_mask; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_CULL_MASK); +} + +uint32_t ParticlesStorage::particles_collision_get_cull_mask(RID p_particles_collision) const { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL_V(particles_collision, 0); + return particles_collision->cull_mask; } void ParticlesStorage::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index 2c96e37a319..662592cea4b 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -436,6 +436,7 @@ public: GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override; virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override; + virtual uint32_t particles_collision_get_cull_mask(RID p_particles_collision) const override; _FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); diff --git a/servers/rendering/dummy/storage/particles_storage.h b/servers/rendering/dummy/storage/particles_storage.h index a11f25e3a04..e3e7d6aa061 100644 --- a/servers/rendering/dummy/storage/particles_storage.h +++ b/servers/rendering/dummy/storage/particles_storage.h @@ -115,6 +115,7 @@ public: virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override { return false; } virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override { return 0; } virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override {} + virtual uint32_t particles_collision_get_cull_mask(RID p_particles_collision) const override { return 0; } virtual RID particles_collision_instance_create(RID p_collision) override { return RID(); } virtual void particles_collision_instance_free(RID p_rid) override {} diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index edbebbd14c3..1259538d9dd 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1120,9 +1120,6 @@ void main() { uvec2 decal_indices = instances.data[draw_call.instance_index].decals; for (uint i = 0; i < sc_decals(); i++) { uint decal_index = (i > 3) ? ((decal_indices.y >> ((i - 4) * 8)) & 0xFF) : ((decal_indices.x >> (i * 8)) & 0xFF); - if (!bool(decals.data[decal_index].mask & instances.data[draw_call.instance_index].layer_mask)) { - continue; //not masked - } vec3 uv_local = (decals.data[decal_index].xform * vec4(vertex, 1.0)).xyz; if (any(lessThan(uv_local, vec3(0.0, -1.0, 0.0))) || any(greaterThan(uv_local, vec3(1.0)))) { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index f4398743105..c708ea127ae 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -268,7 +268,7 @@ void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) { light->cull_mask = p_mask; light->version++; - light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_CULL_MASK); } void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) { diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 5765550b100..ffaf8e326aa 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1850,6 +1850,13 @@ void ParticlesStorage::particles_collision_set_cull_mask(RID p_particles_collisi ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_NULL(particles_collision); particles_collision->cull_mask = p_cull_mask; + particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_CULL_MASK); +} + +uint32_t ParticlesStorage::particles_collision_get_cull_mask(RID p_particles_collision) const { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL_V(particles_collision, 0); + return particles_collision->cull_mask; } uint32_t ParticlesStorage::particles_collision_get_height_field_mask(RID p_particles_collision) const { diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index ecd0921a3da..60f4a24bc9f 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -583,6 +583,7 @@ public: RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override; virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override; + virtual uint32_t particles_collision_get_cull_mask(RID p_particles_collision) const override; Dependency *particles_collision_get_dependency(RID p_particles) const; diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 2a41543410d..f01edd2d2df 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -2744,7 +2744,7 @@ void TextureStorage::decal_set_cull_mask(RID p_decal, uint32_t p_layers) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_NULL(decal); decal->cull_mask = p_layers; - decal->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_DECAL); + decal->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_CULL_MASK); } void TextureStorage::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) { diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 277ada1e4d0..06eeb427557 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -178,6 +178,11 @@ void RendererSceneCull::_instance_pair(Instance *p_A, Instance *p_B) { InstanceLightData *light = static_cast(B->base_data); InstanceGeometryData *geom = static_cast(A->base_data); + if (!(light->cull_mask & A->layer_mask)) { + // Early return if the object's layer mask doesn't match the light's cull mask. + return; + } + geom->lights.insert(B); light->geometries.insert(A); @@ -222,6 +227,11 @@ void RendererSceneCull::_instance_pair(Instance *p_A, Instance *p_B) { InstanceDecalData *decal = static_cast(B->base_data); InstanceGeometryData *geom = static_cast(A->base_data); + if (!(decal->cull_mask & A->layer_mask)) { + // Early return if the object's layer mask doesn't match the decal's cull mask. + return; + } + geom->decals.insert(B); decal->geometries.insert(A); @@ -267,7 +277,10 @@ void RendererSceneCull::_instance_pair(Instance *p_A, Instance *p_B) { voxel_gi->lights.insert(A); } else if (B->base_type == RS::INSTANCE_PARTICLES_COLLISION && A->base_type == RS::INSTANCE_PARTICLES) { InstanceParticlesCollisionData *collision = static_cast(B->base_data); - RSG::particles_storage->particles_add_collision(A->base, collision->instance); + + if ((collision->cull_mask & A->layer_mask)) { + RSG::particles_storage->particles_add_collision(A->base, collision->instance); + } } } @@ -285,6 +298,11 @@ void RendererSceneCull::_instance_unpair(Instance *p_A, Instance *p_B) { InstanceLightData *light = static_cast(B->base_data); InstanceGeometryData *geom = static_cast(A->base_data); + if (!(light->cull_mask & A->layer_mask)) { + // Early return if the object's layer mask doesn't match the light's cull mask. + return; + } + geom->lights.erase(B); light->geometries.erase(A); @@ -339,6 +357,11 @@ void RendererSceneCull::_instance_unpair(Instance *p_A, Instance *p_B) { InstanceDecalData *decal = static_cast(B->base_data); InstanceGeometryData *geom = static_cast(A->base_data); + if (!(decal->cull_mask & A->layer_mask)) { + // Early return if the object's layer mask doesn't match the decal's cull mask. + return; + } + geom->decals.erase(B); decal->geometries.erase(A); @@ -383,7 +406,10 @@ void RendererSceneCull::_instance_unpair(Instance *p_A, Instance *p_B) { voxel_gi->lights.erase(A); } else if (B->base_type == RS::INSTANCE_PARTICLES_COLLISION && A->base_type == RS::INSTANCE_PARTICLES) { InstanceParticlesCollisionData *collision = static_cast(B->base_data); - RSG::particles_storage->particles_remove_collision(A->base, collision->instance); + + if ((collision->cull_mask & A->layer_mask)) { + RSG::particles_storage->particles_remove_collision(A->base, collision->instance); + } } } @@ -888,6 +914,14 @@ void RendererSceneCull::instance_set_layer_mask(RID p_instance, uint32_t p_mask) return; } + // Particles always need to be unpaired. Geometry may need to be unpaired, but only if lights or decals use pairing. + // Needs to happen before layer mask changes so we can avoid attempting to unpair something that was never paired. + if (instance->base_type == RS::INSTANCE_PARTICLES || + (((geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT)) || (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL))) && ((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK))) { + _unpair_instance(instance); + singleton->_instance_queue_update(instance, false, false); + } + instance->layer_mask = p_mask; if (instance->scenario && instance->array_index >= 0) { instance->scenario->instance_data[instance->array_index].layer_mask = p_mask; @@ -1592,6 +1626,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) const { if (light->max_sdfgi_cascade != max_sdfgi_cascade) { light->max_sdfgi_cascade = max_sdfgi_cascade; //should most likely make sdfgi dirty in scenario } + light->cull_mask = RSG::light_storage->light_get_cull_mask(p_instance->base); } else if (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE) { InstanceReflectionProbeData *reflection_probe = static_cast(p_instance->base_data); @@ -1605,6 +1640,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) const { InstanceDecalData *decal = static_cast(p_instance->base_data); RSG::texture_storage->decal_instance_set_transform(decal->instance, *instance_xform); + decal->cull_mask = RSG::texture_storage->decal_get_cull_mask(p_instance->base); } else if (p_instance->base_type == RS::INSTANCE_LIGHTMAP) { InstanceLightmapData *lightmap = static_cast(p_instance->base_data); @@ -1623,6 +1659,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) const { heightfield_particle_colliders_update_list.insert(p_instance); } RSG::particles_storage->particles_collision_instance_set_transform(collision->instance, *instance_xform); + collision->cull_mask = RSG::particles_storage->particles_collision_get_cull_mask(p_instance->base); } else if (p_instance->base_type == RS::INSTANCE_FOG_VOLUME) { InstanceFogVolumeData *volume = static_cast(p_instance->base_data); scene_render->fog_volume_instance_set_transform(volume->instance, *instance_xform); @@ -1818,7 +1855,6 @@ void RendererSceneCull::_update_instance(Instance *p_instance) const { pair.pair_allocator = &pair_allocator; pair.pair_pass = pair_pass; pair.pair_mask = 0; - pair.cull_mask = 0xFFFFFFFF; if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { pair.pair_mask |= 1 << RS::INSTANCE_LIGHT; @@ -1840,7 +1876,6 @@ void RendererSceneCull::_update_instance(Instance *p_instance) const { pair.pair_mask |= (1 << RS::INSTANCE_VOXEL_GI); pair.bvh2 = &p_instance->scenario->indexers[Scenario::INDEXER_VOLUMES]; } - pair.cull_mask = RSG::light_storage->light_get_cull_mask(p_instance->base); } else if (p_instance->base_type == RS::INSTANCE_LIGHTMAP) { pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK; pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY]; @@ -1850,7 +1885,6 @@ void RendererSceneCull::_update_instance(Instance *p_instance) const { } else if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (p_instance->base_type == RS::INSTANCE_DECAL)) { pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK; pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY]; - pair.cull_mask = RSG::texture_storage->decal_get_cull_mask(p_instance->base); } else if (p_instance->base_type == RS::INSTANCE_PARTICLES_COLLISION) { pair.pair_mask = (1 << RS::INSTANCE_PARTICLES); pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY]; diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 42a75891966..0e1141761b6 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -508,7 +508,8 @@ public: case Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE: { singleton->_instance_queue_update(instance, true, true); } break; - case Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR: { + case Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR: + case Dependency::DEPENDENCY_CHANGED_CULL_MASK: { //requires repairing if (instance->indexer_id.is_valid()) { singleton->_unpair_instance(instance); @@ -655,6 +656,7 @@ public: struct InstanceDecalData : public InstanceBaseData { Instance *owner = nullptr; RID instance; + uint32_t cull_mask = 0xFFFFFFFF; HashSet geometries; @@ -666,6 +668,7 @@ public: struct InstanceParticlesCollisionData : public InstanceBaseData { RID instance; + uint32_t cull_mask = 0xFFFFFFFF; }; struct InstanceFogVolumeData : public InstanceBaseData { @@ -699,6 +702,7 @@ public: RS::LightBakeMode bake_mode; uint32_t max_sdfgi_cascade = 2; + uint32_t cull_mask = 0xFFFFFFFF; private: // Instead of a single dirty flag, we maintain a count @@ -817,12 +821,11 @@ public: DynamicBVH *bvh2 = nullptr; //some may need to cull in two uint32_t pair_mask; uint64_t pair_pass; - uint32_t cull_mask = 0xFFFFFFFF; // Needed for decals and lights in the mobile and compatibility renderers. _FORCE_INLINE_ bool operator()(void *p_data) { Instance *p_instance = (Instance *)p_data; - if (instance != p_instance && instance->transformed_aabb.intersects(p_instance->transformed_aabb) && (pair_mask & (1 << p_instance->base_type)) && (cull_mask & p_instance->layer_mask)) { + if (instance != p_instance && instance->transformed_aabb.intersects(p_instance->transformed_aabb) && (pair_mask & (1 << p_instance->base_type))) { //test is more coarse in indexer p_instance->pair_check = pair_pass; InstancePair *pair = pair_allocator->alloc(); diff --git a/servers/rendering/storage/particles_storage.h b/servers/rendering/storage/particles_storage.h index 900bf4790ad..5f96868d514 100644 --- a/servers/rendering/storage/particles_storage.h +++ b/servers/rendering/storage/particles_storage.h @@ -121,6 +121,7 @@ public: virtual bool particles_collision_is_heightfield(RID p_particles_collision) const = 0; virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const = 0; virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) = 0; + virtual uint32_t particles_collision_get_cull_mask(RID p_particles_collision) const = 0; //used from 2D and 3D virtual RID particles_collision_instance_create(RID p_collision) = 0; diff --git a/servers/rendering/storage/utilities.h b/servers/rendering/storage/utilities.h index 54b84fdf760..74d1cb66da4 100644 --- a/servers/rendering/storage/utilities.h +++ b/servers/rendering/storage/utilities.h @@ -50,6 +50,7 @@ public: DEPENDENCY_CHANGED_LIGHT, DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR, DEPENDENCY_CHANGED_REFLECTION_PROBE, + DEPENDENCY_CHANGED_CULL_MASK, }; void changed_notify(DependencyChangedNotification p_notification);