summaryrefslogtreecommitdiffstats
path: root/drivers/gles3/storage/light_storage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gles3/storage/light_storage.cpp')
-rw-r--r--drivers/gles3/storage/light_storage.cpp476
1 files changed, 471 insertions, 5 deletions
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 2fa99707b0..ff06fbfa41 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -31,6 +31,8 @@
#ifdef GLES3_ENABLED
#include "light_storage.h"
+#include "../rasterizer_gles3.h"
+#include "../rasterizer_scene_gles3.h"
#include "config.h"
#include "texture_storage.h"
@@ -358,6 +360,20 @@ RID LightStorage::light_instance_create(RID p_light) {
void LightStorage::light_instance_free(RID p_light_instance) {
LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL(light_instance);
+
+ // Remove from shadow atlases.
+ for (const RID &E : light_instance->shadow_atlases) {
+ ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(E);
+ ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_light_instance));
+ uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
+ uint32_t q = (key >> QUADRANT_SHIFT) & 0x3;
+ uint32_t s = key & SHADOW_INDEX_MASK;
+
+ shadow_atlas->quadrants[q].shadows.write[s].owner = RID();
+
+ shadow_atlas->shadow_owners.erase(p_light_instance);
+ }
+
light_instance_owner.free(p_light_instance);
}
@@ -376,9 +392,26 @@ void LightStorage::light_instance_set_aabb(RID p_light_instance, const AABB &p_a
}
void LightStorage::light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) {
+ LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
+ ERR_FAIL_NULL(light_instance);
+
+ ERR_FAIL_INDEX(p_pass, 6);
+
+ light_instance->shadow_transform[p_pass].camera = p_projection;
+ light_instance->shadow_transform[p_pass].transform = p_transform;
+ light_instance->shadow_transform[p_pass].farplane = p_far;
+ light_instance->shadow_transform[p_pass].split = p_split;
+ light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale;
+ light_instance->shadow_transform[p_pass].range_begin = p_range_begin;
+ light_instance->shadow_transform[p_pass].shadow_texel_size = p_shadow_texel_size;
+ light_instance->shadow_transform[p_pass].uv_scale = p_uv_scale;
}
void LightStorage::light_instance_mark_visible(RID p_light_instance) {
+ LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
+ ERR_FAIL_NULL(light_instance);
+
+ light_instance->last_scene_pass = RasterizerSceneGLES3::get_singleton()->get_scene_pass();
}
/* PROBE API */
@@ -598,33 +631,466 @@ void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transfo
/* SHADOW ATLAS API */
RID LightStorage::shadow_atlas_create() {
- return RID();
+ return shadow_atlas_owner.make_rid(ShadowAtlas());
}
void LightStorage::shadow_atlas_free(RID p_atlas) {
+ shadow_atlas_set_size(p_atlas, 0);
+ shadow_atlas_owner.free(p_atlas);
}
void LightStorage::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) {
+ ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas);
+ ERR_FAIL_NULL(shadow_atlas);
+ ERR_FAIL_COND(p_size < 0);
+ p_size = next_power_of_2(p_size);
+
+ if (p_size == shadow_atlas->size && p_16_bits == shadow_atlas->use_16_bits) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < 4; i++) {
+ // Clear all subdivisions and free shadows.
+ for (uint32_t j = 0; j < shadow_atlas->quadrants[i].textures.size(); j++) {
+ glDeleteTextures(1, &shadow_atlas->quadrants[i].textures[j]);
+ glDeleteFramebuffers(1, &shadow_atlas->quadrants[i].fbos[j]);
+ }
+ shadow_atlas->quadrants[i].textures.clear();
+ shadow_atlas->quadrants[i].fbos.clear();
+
+ shadow_atlas->quadrants[i].shadows.clear();
+ shadow_atlas->quadrants[i].shadows.resize(shadow_atlas->quadrants[i].subdivision * shadow_atlas->quadrants[i].subdivision);
+ }
+
+ // Erase shadow atlas reference from lights.
+ for (const KeyValue<RID, uint32_t> &E : shadow_atlas->shadow_owners) {
+ LightInstance *li = light_instance_owner.get_or_null(E.key);
+ ERR_CONTINUE(!li);
+ li->shadow_atlases.erase(p_atlas);
+ }
+
+ if (shadow_atlas->debug_texture != 0) {
+ glDeleteTextures(1, &shadow_atlas->debug_texture);
+ }
+
+ if (shadow_atlas->debug_fbo != 0) {
+ glDeleteFramebuffers(1, &shadow_atlas->debug_fbo);
+ }
+
+ // Clear owners.
+ shadow_atlas->shadow_owners.clear();
+
+ shadow_atlas->size = p_size;
+ shadow_atlas->use_16_bits = p_16_bits;
}
void LightStorage::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) {
+ ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas);
+ ERR_FAIL_NULL(shadow_atlas);
+ ERR_FAIL_INDEX(p_quadrant, 4);
+ ERR_FAIL_INDEX(p_subdivision, 16384);
+
+ uint32_t subdiv = next_power_of_2(p_subdivision);
+ if (subdiv & 0xaaaaaaaa) { // sqrt(subdiv) must be integer.
+ subdiv <<= 1;
+ }
+
+ subdiv = int(Math::sqrt((float)subdiv));
+
+ if (shadow_atlas->quadrants[p_quadrant].subdivision == subdiv) {
+ return;
+ }
+
+ // Erase all data from quadrant.
+ for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) {
+ if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) {
+ shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
+ LightInstance *li = light_instance_owner.get_or_null(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
+ ERR_CONTINUE(!li);
+ li->shadow_atlases.erase(p_atlas);
+ }
+ }
+
+ for (uint32_t j = 0; j < shadow_atlas->quadrants[p_quadrant].textures.size(); j++) {
+ glDeleteTextures(1, &shadow_atlas->quadrants[p_quadrant].textures[j]);
+ glDeleteFramebuffers(1, &shadow_atlas->quadrants[p_quadrant].fbos[j]);
+ }
+
+ shadow_atlas->quadrants[p_quadrant].textures.clear();
+ shadow_atlas->quadrants[p_quadrant].fbos.clear();
+
+ shadow_atlas->quadrants[p_quadrant].shadows.clear();
+ shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv * subdiv);
+ shadow_atlas->quadrants[p_quadrant].subdivision = subdiv;
+
+ // Cache the smallest subdiv (for faster allocation in light update).
+
+ shadow_atlas->smallest_subdiv = 1 << 30;
+
+ for (int i = 0; i < 4; i++) {
+ if (shadow_atlas->quadrants[i].subdivision) {
+ shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision);
+ }
+ }
+
+ if (shadow_atlas->smallest_subdiv == 1 << 30) {
+ shadow_atlas->smallest_subdiv = 0;
+ }
+
+ // Re-sort the size orders, simple bubblesort for 4 elements.
+
+ int swaps = 0;
+ do {
+ swaps = 0;
+
+ for (int i = 0; i < 3; i++) {
+ if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) {
+ SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]);
+ swaps++;
+ }
+ }
+ } while (swaps > 0);
+}
+
+bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) {
+ ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas);
+ ERR_FAIL_NULL_V(shadow_atlas, false);
+
+ LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
+ ERR_FAIL_NULL_V(li, false);
+
+ if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) {
+ return false;
+ }
+
+ uint32_t quad_size = shadow_atlas->size >> 1;
+ int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage));
+
+ int valid_quadrants[4];
+ int valid_quadrant_count = 0;
+ int best_size = -1; // Best size found.
+ int best_subdiv = -1; // Subdiv for the best size.
+
+ // Find the quadrants this fits into, and the best possible size it can fit into.
+ for (int i = 0; i < 4; i++) {
+ int q = shadow_atlas->size_order[i];
+ int sd = shadow_atlas->quadrants[q].subdivision;
+ if (sd == 0) {
+ continue; // Unused.
+ }
+
+ int max_fit = quad_size / sd;
+
+ if (best_size != -1 && max_fit > best_size) {
+ break; // Too large.
+ }
+
+ valid_quadrants[valid_quadrant_count++] = q;
+ best_subdiv = sd;
+
+ if (max_fit >= desired_fit) {
+ best_size = max_fit;
+ }
+ }
+
+ ERR_FAIL_COND_V(valid_quadrant_count == 0, false);
+
+ uint64_t tick = OS::get_singleton()->get_ticks_msec();
+
+ uint32_t old_key = SHADOW_INVALID;
+ uint32_t old_quadrant = SHADOW_INVALID;
+ uint32_t old_shadow = SHADOW_INVALID;
+ int old_subdivision = -1;
+
+ bool should_realloc = false;
+ bool should_redraw = false;
+
+ if (shadow_atlas->shadow_owners.has(p_light_instance)) {
+ old_key = shadow_atlas->shadow_owners[p_light_instance];
+ old_quadrant = (old_key >> QUADRANT_SHIFT) & 0x3;
+ old_shadow = old_key & SHADOW_INDEX_MASK;
+
+ // Only re-allocate if a better option is available, and enough time has passed.
+ should_realloc = shadow_atlas->quadrants[old_quadrant].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec);
+ should_redraw = shadow_atlas->quadrants[old_quadrant].shadows[old_shadow].version != p_light_version;
+
+ if (!should_realloc) {
+ shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].version = p_light_version;
+ // Already existing, see if it should redraw or it's just OK.
+ return should_redraw;
+ }
+
+ old_subdivision = shadow_atlas->quadrants[old_quadrant].subdivision;
+ }
+
+ bool is_omni = li->light_type == RS::LIGHT_OMNI;
+ bool found_shadow = false;
+ int new_quadrant = -1;
+ int new_shadow = -1;
+
+ found_shadow = _shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, old_subdivision, tick, is_omni, new_quadrant, new_shadow);
+
+ // For new shadows if we found an atlas.
+ // Or for existing shadows that found a better atlas.
+ if (found_shadow) {
+ if (old_quadrant != SHADOW_INVALID) {
+ shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].version = 0;
+ shadow_atlas->quadrants[old_quadrant].shadows.write[old_shadow].owner = RID();
+ }
+
+ uint32_t new_key = new_quadrant << QUADRANT_SHIFT;
+ new_key |= new_shadow;
+
+ ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow];
+ _shadow_atlas_invalidate_shadow(sh, p_atlas, shadow_atlas, new_quadrant, new_shadow);
+
+ sh->owner = p_light_instance;
+ sh->owner_is_omni = is_omni;
+ sh->alloc_tick = tick;
+ sh->version = p_light_version;
+
+ li->shadow_atlases.insert(p_atlas);
+
+ // Update it in map.
+ shadow_atlas->shadow_owners[p_light_instance] = new_key;
+ // Make it dirty, as it should redraw anyway.
+ return true;
+ }
+
+ return should_redraw;
}
-bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) {
+bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, bool is_omni, int &r_quadrant, int &r_shadow) {
+ for (int i = p_quadrant_count - 1; i >= 0; i--) {
+ int qidx = p_in_quadrants[i];
+
+ if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) {
+ return false;
+ }
+
+ // Look for an empty space.
+ int sc = shadow_atlas->quadrants[qidx].shadows.size();
+ const ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptr();
+
+ // We have a free space in this quadrant, allocate a texture and use it.
+ if (sc > (int)shadow_atlas->quadrants[qidx].textures.size()) {
+ GLuint fbo_id = 0;
+ glGenFramebuffers(1, &fbo_id);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id);
+
+ GLuint texture_id = 0;
+ glGenTextures(1, &texture_id);
+ glActiveTexture(GL_TEXTURE0);
+
+ int size = (shadow_atlas->size >> 1) / shadow_atlas->quadrants[qidx].subdivision;
+
+ GLenum format = shadow_atlas->use_16_bits ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24;
+ GLenum type = shadow_atlas->use_16_bits ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
+
+ if (is_omni) {
+ glBindTexture(GL_TEXTURE_CUBE_MAP, texture_id);
+ for (int id = 0; id < 6; id++) {
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + id, 0, format, size / 2, size / 2, 0, GL_DEPTH_COMPONENT, type, nullptr);
+ }
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texture_id, 0);
+
+#ifdef DEBUG_ENABLED
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ ERR_PRINT("Could not create omni light shadow framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
+ }
+#endif
+ glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+ } else {
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, format, size, size, 0, GL_DEPTH_COMPONENT, type, nullptr);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture_id, 0);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ r_quadrant = qidx;
+ r_shadow = shadow_atlas->quadrants[qidx].textures.size();
+
+ shadow_atlas->quadrants[qidx].textures.push_back(texture_id);
+ shadow_atlas->quadrants[qidx].fbos.push_back(fbo_id);
+
+ return true;
+ }
+
+ int found_used_idx = -1; // Found existing one, must steal it.
+ uint64_t min_pass = 0; // Pass of the existing one, try to use the least recently used one (LRU fashion).
+
+ for (int j = 0; j < sc; j++) {
+ LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner);
+ ERR_CONTINUE(!sli);
+
+ if (sli->last_scene_pass != RasterizerSceneGLES3::get_singleton()->get_scene_pass()) {
+ // Was just allocated, don't kill it so soon, wait a bit.
+ if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) {
+ continue;
+ }
+
+ if (found_used_idx == -1 || sli->last_scene_pass < min_pass) {
+ found_used_idx = j;
+ min_pass = sli->last_scene_pass;
+ }
+ }
+ }
+
+ if (found_used_idx != -1) {
+ r_quadrant = qidx;
+ r_shadow = found_used_idx;
+
+ return true;
+ }
+ }
+
return false;
}
+void LightStorage::_shadow_atlas_invalidate_shadow(ShadowAtlas::Quadrant::Shadow *p_shadow, RID p_atlas, ShadowAtlas *p_shadow_atlas, uint32_t p_quadrant, uint32_t p_shadow_idx) {
+ if (p_shadow->owner.is_valid()) {
+ LightInstance *sli = light_instance_owner.get_or_null(p_shadow->owner);
+
+ p_shadow_atlas->shadow_owners.erase(p_shadow->owner);
+ p_shadow->version = 0;
+ p_shadow->owner = RID();
+ sli->shadow_atlases.erase(p_atlas);
+ }
+}
+
void LightStorage::shadow_atlas_update(RID p_atlas) {
+ // Do nothing as there is no shadow atlas texture.
}
-void LightStorage::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) {
+/* DIRECTIONAL SHADOW */
+
+// Create if necessary and clear.
+void LightStorage::update_directional_shadow_atlas() {
+ if (directional_shadow.depth == 0 && directional_shadow.size > 0) {
+ glGenFramebuffers(1, &directional_shadow.fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
+
+ glGenTextures(1, &directional_shadow.depth);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
+
+ GLenum format = directional_shadow.use_16_bits ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24;
+ GLenum type = directional_shadow.use_16_bits ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
+
+ glTexImage2D(GL_TEXTURE_2D, 0, format, directional_shadow.size, directional_shadow.size, 0, GL_DEPTH_COMPONENT, type, nullptr);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0);
+ }
+ glDepthMask(GL_TRUE);
+ glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
+ RasterizerGLES3::clear_depth(1.0);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-int LightStorage::get_directional_light_shadow_size(RID p_light_intance) {
- return 0;
+void LightStorage::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) {
+ p_size = nearest_power_of_2_templated(p_size);
+
+ if (directional_shadow.size == p_size && directional_shadow.use_16_bits == p_16_bits) {
+ return;
+ }
+
+ directional_shadow.size = p_size;
+ directional_shadow.use_16_bits = p_16_bits;
+
+ if (directional_shadow.depth != 0) {
+ glDeleteTextures(1, &directional_shadow.depth);
+ directional_shadow.depth = 0;
+ glDeleteFramebuffers(1, &directional_shadow.fbo);
+ directional_shadow.fbo = 0;
+ }
}
void LightStorage::set_directional_shadow_count(int p_count) {
+ directional_shadow.light_count = p_count;
+ directional_shadow.current_light = 0;
+}
+
+static Rect2i _get_directional_shadow_rect(int p_size, int p_shadow_count, int p_shadow_index) {
+ int split_h = 1;
+ int split_v = 1;
+
+ while (split_h * split_v < p_shadow_count) {
+ if (split_h == split_v) {
+ split_h <<= 1;
+ } else {
+ split_v <<= 1;
+ }
+ }
+
+ Rect2i rect(0, 0, p_size, p_size);
+ rect.size.width /= split_h;
+ rect.size.height /= split_v;
+
+ rect.position.x = rect.size.width * (p_shadow_index % split_h);
+ rect.position.y = rect.size.height * (p_shadow_index / split_h);
+
+ return rect;
+}
+
+Rect2i LightStorage::get_directional_shadow_rect() {
+ return _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, directional_shadow.current_light);
+}
+
+int LightStorage::get_directional_light_shadow_size(RID p_light_instance) {
+ ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0);
+
+ Rect2i r = _get_directional_shadow_rect(directional_shadow.size, directional_shadow.light_count, 0);
+
+ LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
+ ERR_FAIL_NULL_V(light_instance, 0);
+
+ switch (light_directional_get_shadow_mode(light_instance->light)) {
+ case RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL:
+ break; //none
+ case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS:
+ r.size.height /= 2;
+ break;
+ case RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS:
+ r.size /= 2;
+ break;
+ }
+
+ return MAX(r.size.width, r.size.height);
}
#endif // !GLES3_ENABLED