mirror of
https://github.com/godotengine/godot.git
synced 2026-02-07 19:32:36 +00:00
Merge pull request #107234 from jon1solution/select-near-lights-gles3-mobile
Select relevant 3D lights per mesh on GLES3 and Mobile renderers
This commit is contained in:
@@ -72,27 +72,39 @@ 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();
|
||||
uint32_t RasterizerSceneGLES3::get_max_lights_total() {
|
||||
return (uint32_t)GLES3::Config::get_singleton()->max_renderable_lights;
|
||||
}
|
||||
|
||||
uint32_t RasterizerSceneGLES3::get_max_lights_per_mesh() {
|
||||
return (uint32_t)GLES3::Config::get_singleton()->max_lights_per_object;
|
||||
}
|
||||
|
||||
void 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) {
|
||||
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:
|
||||
|
||||
@@ -353,7 +353,8 @@ private:
|
||||
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 void 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 {}
|
||||
@@ -361,6 +362,9 @@ private:
|
||||
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override {}
|
||||
};
|
||||
|
||||
virtual uint32_t get_max_lights_total() override;
|
||||
virtual uint32_t get_max_lights_per_mesh() override;
|
||||
|
||||
enum {
|
||||
INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3,
|
||||
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
|
||||
|
||||
@@ -66,7 +66,8 @@ public:
|
||||
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 void 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 {}
|
||||
@@ -95,6 +96,9 @@ public:
|
||||
|
||||
uint32_t geometry_instance_get_pair_mask() override { return 0; }
|
||||
|
||||
virtual uint32_t get_max_lights_total() override { return 0; }
|
||||
virtual uint32_t get_max_lights_per_mesh() override { return 0; }
|
||||
|
||||
/* PIPELINES */
|
||||
|
||||
virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) override {}
|
||||
|
||||
@@ -66,7 +66,8 @@ public:
|
||||
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 void 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;
|
||||
|
||||
@@ -589,7 +589,8 @@ private:
|
||||
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 void 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;
|
||||
@@ -597,6 +598,10 @@ private:
|
||||
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override;
|
||||
};
|
||||
|
||||
// These are not used in the Forward+ path, it has different light clustering tech.
|
||||
virtual uint32_t get_max_lights_total() override { return 0; }
|
||||
virtual uint32_t get_max_lights_per_mesh() override { return 0; }
|
||||
|
||||
static void _geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker);
|
||||
static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
|
||||
|
||||
|
||||
@@ -2653,23 +2653,34 @@ 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) {
|
||||
uint32_t RenderForwardMobile::get_max_lights_total() {
|
||||
return (uint32_t)get_singleton()->get_max_elements();
|
||||
}
|
||||
|
||||
uint32_t RenderForwardMobile::get_max_lights_per_mesh() {
|
||||
return (uint32_t)MAX_RDL_CULL;
|
||||
}
|
||||
|
||||
void 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) {
|
||||
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:
|
||||
|
||||
@@ -566,7 +566,8 @@ protected:
|
||||
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 void 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 {}
|
||||
@@ -574,6 +575,9 @@ protected:
|
||||
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override;
|
||||
};
|
||||
|
||||
virtual uint32_t get_max_lights_total() override;
|
||||
virtual uint32_t get_max_lights_per_mesh() override;
|
||||
|
||||
/* Rendering */
|
||||
|
||||
virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override;
|
||||
|
||||
@@ -1957,7 +1957,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);
|
||||
@@ -2823,6 +2823,14 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul
|
||||
|
||||
RID instance_pair_buffer[MAX_INSTANCE_PAIRS];
|
||||
|
||||
// Minimize allocations when picking the most relevant lights per mesh.
|
||||
// We need to track the score and current index of the best N lights.
|
||||
thread_local LocalVector<Pair<float, uint32_t>> omni_score_idx, spot_score_idx;
|
||||
omni_score_idx.clear();
|
||||
spot_score_idx.clear();
|
||||
uint32_t max_lights_per_mesh = scene_render->get_max_lights_per_mesh();
|
||||
uint32_t max_lights_total = scene_render->get_max_lights_total();
|
||||
|
||||
Transform3D inv_cam_transform = cull_data.cam_transform.inverse();
|
||||
float z_near = cull_data.camera_matrix->get_z_near();
|
||||
bool is_orthogonal = cull_data.camera_matrix->is_orthogonal();
|
||||
@@ -2934,26 +2942,109 @@ 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;
|
||||
ERR_FAIL_NULL(geom->geometry_instance);
|
||||
// Clear any existing light instances for this mesh and find the max count per-mesh, and total (per-scene).
|
||||
geom->geometry_instance->clear_light_instances();
|
||||
if ((max_lights_per_mesh > 0) && (max_lights_total > 0)) {
|
||||
// For the top N lights, track the score and the index into the internal light storage array.
|
||||
uint32_t total_omni_count = 0, total_spot_count = 0;
|
||||
bool omni_needs_heap = true, spot_needs_heap = true;
|
||||
uint32_t omni_count = 0, spot_count = 0;
|
||||
omni_score_idx.clear();
|
||||
spot_score_idx.clear();
|
||||
SortArray<Pair<float, uint32_t>> heapify; // SortArray has heap functions, but no local storage.
|
||||
// Iterate over the lights (possibly > max_renderable_lights), keeping the closest to the mesh center.
|
||||
Vector3 mesh_center = idata.instance->transformed_aabb.get_center();
|
||||
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_lights_total)) ||
|
||||
((RS::LIGHT_SPOT == light_type) && (total_spot_count++ < max_lights_total))) {
|
||||
// Perform culling.
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
// Of the N lights (on a per-light-type basis, Omni or Spot) keep only the M "best" lights.
|
||||
// If N <= M, we can simply store the lights, but once we exceed M, we need check each new
|
||||
// light and see if it's score is better than the worst light stored to date. If the new
|
||||
// light is better, we can replace the current worst light with the new one. In order to
|
||||
// efficiently track our currently worst light we use a "max heap". This loosely orders
|
||||
// the elements in an array as a binary-tree structure, and has the properties that finding
|
||||
// the worst score element is O(1) (it will always be stored in element [0]), and removing
|
||||
// the old max and inserting a new value is O(log M).
|
||||
#define VERIFY_RELEVANT_LIGHT_HEAP 0
|
||||
#if VERIFY_RELEVANT_LIGHT_HEAP
|
||||
WARN_PRINT_ONCE("VERIFY_RELEVANT_LIGHT_HEAP is True");
|
||||
#endif
|
||||
switch (light_type) {
|
||||
case RS::LIGHT_OMNI: {
|
||||
if (omni_count < max_lights_per_mesh) {
|
||||
// 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) {
|
||||
#if VERIFY_RELEVANT_LIGHT_HEAP
|
||||
// The [0] element should have the max score.
|
||||
for (uint32_t vi = 1; vi < max_lights_per_mesh; ++vi) {
|
||||
if (omni_score_idx[vi].first > omni_score_idx[0].first) {
|
||||
ERR_PRINT_ONCE("Relevant Omni Light Heap Error");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
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_lights_per_mesh) {
|
||||
// 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) {
|
||||
#if VERIFY_RELEVANT_LIGHT_HEAP
|
||||
// The [0] element should have the max score.
|
||||
for (uint32_t vi = 1; vi < max_lights_per_mesh; ++vi) {
|
||||
if (spot_score_idx[vi].first > spot_score_idx[0].first) {
|
||||
ERR_PRINT_ONCE("Relevant Spot Light Heap Error");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ public:
|
||||
virtual void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) = 0;
|
||||
virtual uint32_t geometry_instance_get_pair_mask() = 0;
|
||||
|
||||
/* Lights matched up to Geometry Instances */
|
||||
virtual uint32_t get_max_lights_total() = 0;
|
||||
virtual uint32_t get_max_lights_per_mesh() = 0;
|
||||
|
||||
/* PIPELINES */
|
||||
|
||||
virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) = 0;
|
||||
|
||||
Reference in New Issue
Block a user