diff options
Diffstat (limited to 'drivers/gles3/storage/light_storage.cpp')
-rw-r--r-- | drivers/gles3/storage/light_storage.cpp | 476 |
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 |