Skip to content

Select relevant 3D lights per mesh on GLES3 and Mobile renderers #107234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,27 +71,32 @@ uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() {
return ((1 << RS::INSTANCE_LIGHT) | (1 << RS::INSTANCE_REFLECTION_PROBE));
}

void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) {
GLES3::Config *config = GLES3::Config::get_singleton();

Pair<uint32_t, uint32_t> RasterizerSceneGLES3::GeometryInstanceGLES3::clear_light_instances() {
paired_omni_light_count = 0;
paired_spot_light_count = 0;
paired_omni_lights.clear();
paired_spot_lights.clear();

for (uint32_t i = 0; i < p_light_instance_count; i++) {
RS::LightType type = GLES3::LightStorage::get_singleton()->light_instance_get_type(p_light_instances[i]);
switch (type) {
return Pair((uint32_t)GLES3::Config::get_singleton()->max_lights_per_object,
(uint32_t)GLES3::Config::get_singleton()->max_renderable_lights);
}
void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instance(
const RID p_light_instance, RS::LightType light_type, uint32_t placement_idx) {
if (placement_idx < GLES3::Config::get_singleton()->max_lights_per_object) {
switch (light_type) {
case RS::LIGHT_OMNI: {
if (paired_omni_light_count < (uint32_t)config->max_lights_per_object) {
paired_omni_lights.push_back(p_light_instances[i]);
paired_omni_light_count++;
if (placement_idx >= paired_omni_light_count) {
paired_omni_lights.push_back(p_light_instance);
++paired_omni_light_count;
} else {
paired_omni_lights[placement_idx] = p_light_instance;
}
} break;
case RS::LIGHT_SPOT: {
if (paired_spot_light_count < (uint32_t)config->max_lights_per_object) {
paired_spot_lights.push_back(p_light_instances[i]);
paired_spot_light_count++;
if (placement_idx >= paired_spot_light_count) {
paired_spot_lights.push_back(p_light_instance);
++paired_spot_light_count;
} else {
paired_spot_lights[placement_idx] = p_light_instance;
}
} break;
default:
Expand Down
3 changes: 2 additions & 1 deletion drivers/gles3/rasterizer_scene_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,8 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
virtual void set_lightmap_capture(const Color *p_sh9) override;

virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
virtual Pair<uint32_t, uint32_t> clear_light_instances() override;
virtual void pair_light_instance(const RID p_light_instance, RS::LightType light_type, uint32_t placement_idx) override;
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
Expand Down
3 changes: 2 additions & 1 deletion servers/rendering/dummy/rasterizer_scene_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class RasterizerSceneDummy : public RendererSceneRender {
virtual Transform3D get_transform() override { return Transform3D(); }
virtual AABB get_aabb() override { return AABB(); }

virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override {}
virtual Pair<uint32_t, uint32_t> clear_light_instances() override { return Pair((uint32_t)0, (uint32_t)0); }
virtual void pair_light_instance(const RID p_light_instance, RS::LightType light_type, uint32_t placement_idx) override {}
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {}
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
Expand Down
3 changes: 2 additions & 1 deletion servers/rendering/renderer_geometry_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class RenderGeometryInstance {
virtual Transform3D get_transform() = 0;
virtual AABB get_aabb() = 0;

virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) = 0;
virtual Pair<uint32_t, uint32_t> clear_light_instances() = 0;
virtual void pair_light_instance(const RID p_light_instance, RS::LightType light_type, uint32_t placement_idx) = 0;
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) = 0;
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) = 0;
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,8 @@ class RenderForwardClustered : public RendererSceneRenderRD {
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
virtual void set_lightmap_capture(const Color *p_sh9) override;

virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override {}
virtual Pair<uint32_t, uint32_t> clear_light_instances() override { return Pair((uint32_t)0, (uint32_t)0); }
virtual void pair_light_instance(const RID p_light_instance, RS::LightType light_type, uint32_t placement_idx) override {}
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {}
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2553,23 +2553,27 @@ uint32_t RenderForwardMobile::geometry_instance_get_pair_mask() {
return ((1 << RS::INSTANCE_LIGHT) + (1 << RS::INSTANCE_REFLECTION_PROBE) + (1 << RS::INSTANCE_DECAL));
}

void RenderForwardMobile::GeometryInstanceForwardMobile::pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) {
Pair<uint32_t, uint32_t> RenderForwardMobile::GeometryInstanceForwardMobile::clear_light_instances() {
omni_light_count = 0;
spot_light_count = 0;

for (uint32_t i = 0; i < p_light_instance_count; i++) {
RS::LightType type = RendererRD::LightStorage::get_singleton()->light_instance_get_type(p_light_instances[i]);
switch (type) {
return Pair((uint32_t)MAX_RDL_CULL,
(uint32_t)get_singleton()->get_max_elements());
}
void RenderForwardMobile::GeometryInstanceForwardMobile::pair_light_instance(
const RID p_light_instance, RS::LightType light_type, uint32_t placement_idx) {
if (placement_idx < (uint32_t)MAX_RDL_CULL) {
RendererRD::ForwardID light_id = RendererRD::LightStorage::get_singleton()->light_instance_get_forward_id(p_light_instance);
switch (light_type) {
case RS::LIGHT_OMNI: {
if (omni_light_count < (uint32_t)MAX_RDL_CULL) {
omni_lights[omni_light_count] = RendererRD::LightStorage::get_singleton()->light_instance_get_forward_id(p_light_instances[i]);
omni_light_count++;
omni_lights[placement_idx] = light_id;
if (placement_idx >= omni_light_count) {
omni_light_count = placement_idx + 1;
}
} break;
case RS::LIGHT_SPOT: {
if (spot_light_count < (uint32_t)MAX_RDL_CULL) {
spot_lights[spot_light_count] = RendererRD::LightStorage::get_singleton()->light_instance_get_forward_id(p_light_instances[i]);
spot_light_count++;
spot_lights[placement_idx] = light_id;
if (placement_idx >= spot_light_count) {
spot_light_count = placement_idx + 1;
}
} break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,8 @@ class RenderForwardMobile : public RendererSceneRenderRD {
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
virtual void set_lightmap_capture(const Color *p_sh9) override;

virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
virtual Pair<uint32_t, uint32_t> clear_light_instances() override;
virtual void pair_light_instance(const RID p_light_instance, RS::LightType light_type, uint32_t placement_idx) override;
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override;
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
Expand Down
93 changes: 74 additions & 19 deletions servers/rendering/renderer_scene_cull.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1956,7 +1956,7 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) {
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data);
ERR_FAIL_NULL(geom->geometry_instance);

geom->geometry_instance->pair_light_instances(nullptr, 0);
geom->geometry_instance->clear_light_instances();
geom->geometry_instance->pair_reflection_probe_instances(nullptr, 0);
geom->geometry_instance->pair_decal_instances(nullptr, 0);
geom->geometry_instance->pair_voxel_gi_instances(nullptr, 0);
Expand Down Expand Up @@ -2932,26 +2932,81 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul

if (geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT) && (idata.flags & InstanceData::FLAG_GEOM_LIGHTING_DIRTY)) {
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;

for (const Instance *E : geom->lights) {
InstanceLightData *light = static_cast<InstanceLightData *>(E->base_data);
if (!(RSG::light_storage->light_get_cull_mask(E->base) & idata.layer_mask)) {
continue;
}

if ((RSG::light_storage->light_get_bake_mode(E->base) == RS::LIGHT_BAKE_STATIC) && idata.instance->lightmap) {
continue;
}

instance_pair_buffer[idx++] = light->instance;
if (idx == MAX_INSTANCE_PAIRS) {
break;
ERR_FAIL_NULL(geom->geometry_instance);
// clear any existing light instances for this mesh and return the max count per-mesh, and total (per-scene).
Pair<uint32_t, uint32_t> max_per_mesh_total = geom->geometry_instance->clear_light_instances();
if ((max_per_mesh_total.first > 0) && (max_per_mesh_total.second > 0)) {
// Run through all M lights, but use a heap to keep track of the best N
uint32_t omni_count = 0, spot_count = 0;
LocalVector<Pair<float, uint32_t>> omni_score_idx, spot_score_idx; // score, index into the light array
SortArray<Pair<float, uint32_t>> heapify; // SortArray has heap functionality in it
bool omni_needs_heap = true, spot_needs_heap = true;
Vector3 mesh_center = idata.instance->transformed_aabb.get_center();
// Run through all lights in the scene (there might be more than max_renderable_lights)
uint32_t total_omni_count = 0, total_spot_count = 0;
for (const Instance *E : geom->lights) {
RS::LightType light_type = RSG::light_storage->light_get_type(E->base);
if (((RS::LIGHT_OMNI == light_type) && (total_omni_count++ < max_per_mesh_total.second)) ||
((RS::LIGHT_SPOT == light_type) && (total_spot_count++ < max_per_mesh_total.second))) {
// Cull
if (!(RSG::light_storage->light_get_cull_mask(E->base) & idata.layer_mask)) {
continue;
}
if ((RSG::light_storage->light_get_bake_mode(E->base) == RS::LIGHT_BAKE_STATIC) && idata.instance->lightmap) {
continue;
}

InstanceLightData *light = static_cast<InstanceLightData *>(E->base_data);
// Large scores are worse, so linear with distance, inverse with energy and range
Vector3 light_center = E->transformed_aabb.get_center();
float light_range_energy =
RSG::light_storage->light_get_param(E->base, RS::LightParam::LIGHT_PARAM_RANGE) *
RSG::light_storage->light_get_param(E->base, RS::LightParam::LIGHT_PARAM_ENERGY);
float light_inst_score = mesh_center.distance_to(light_center) / MAX(0.01f, light_range_energy);
// Keep the best per-light-type
switch (light_type) {
case RS::LIGHT_OMNI: {
if (omni_count < max_per_mesh_total.first) {
// We have room to just add it, and track the score and where it goes
omni_score_idx.push_back(Pair(light_inst_score, omni_count));
geom->geometry_instance->pair_light_instance(light->instance, light_type, omni_count++);
} else {
if (omni_needs_heap) {
// We need to make this a heap one time
heapify.make_heap(0, omni_count, &omni_score_idx[0]);
omni_needs_heap = false;
}
if (light_inst_score < omni_score_idx[0].first) {
uint32_t replace_index = omni_score_idx[0].second;
geom->geometry_instance->pair_light_instance(light->instance, light_type, replace_index);
heapify.adjust_heap(0, 0, omni_count, Pair(light_inst_score, replace_index), &omni_score_idx[0]);
}
}
} break;
case RS::LIGHT_SPOT: {
if (spot_count < max_per_mesh_total.first) {
// We have room to just add it, and track the score and where it goes
spot_score_idx.push_back(Pair(light_inst_score, spot_count));
geom->geometry_instance->pair_light_instance(light->instance, light_type, spot_count++);
} else {
if (spot_needs_heap) {
// We need to make this a heap one time
heapify.make_heap(0, spot_count, &spot_score_idx[0]);
spot_needs_heap = false;
}
if (light_inst_score < spot_score_idx[0].first) {
uint32_t replace_index = spot_score_idx[0].second;
geom->geometry_instance->pair_light_instance(light->instance, light_type, replace_index);
heapify.adjust_heap(0, 0, spot_count, Pair(light_inst_score, replace_index), &spot_score_idx[0]);
}
}
} break;
default:
break;
}
}
}
}

ERR_FAIL_NULL(geom->geometry_instance);
geom->geometry_instance->pair_light_instances(instance_pair_buffer, idx);
idata.flags &= ~InstanceData::FLAG_GEOM_LIGHTING_DIRTY;
}

Expand Down