#include "texture-unit-cache.hpp" #include "compat/assert.hpp" #include #include #include #include namespace floormat { struct texture_unit_cache::unit_data final { GL::AbstractTexture* ptr; size_t lru_val; }; int32_t texture_unit_cache::bind(GL::AbstractTexture* tex) { fm_debug_assert(tex != nullptr && tex != (GL::AbstractTexture*)-1); constexpr auto invalid = (size_t)-1; auto unbound_id = invalid; for (auto i = 0uz; i < unit_count; i++) { auto& unit = units[i]; auto* ptr = unit.ptr; if (!ptr) unbound_id = i; else if (ptr == tex) { unit.lru_val = ++lru_counter; ++cache_hit_count; //Debug{Debug::Flag::NoSpace} << "already bound '" << tex->label() << "' to " << i; return (int32_t)i; } } if (unbound_id != invalid) { units[unbound_id] = {tex, ++lru_counter}; tex->bind((Int)unbound_id); ++cache_hit_count; auto label = tex->label(); //Debug{Debug::Flag::NoSpace} << "binding '" << tex->label() << "' to " << unbound_id; return (int32_t)unbound_id; } else { auto min_lru = invalid, min_index = invalid; for (auto i = 0uz; i < unit_count; i++) { auto& unit = units[i]; if (unit.lru_val < min_lru) { min_lru = unit.lru_val; min_index = i; } } fm_assert(min_index != invalid); ++cache_miss_count; units[min_index] = {tex, ++lru_counter}; tex->bind((Int)min_index); //Debug{Debug::Flag::NoSpace} << "rebinding '" << tex->label() << "' to " << min_index; return (int32_t)min_index; } } texture_unit_cache::texture_unit_cache() : unit_count{get_unit_count()}, units{ValueInit, unit_count} { } void texture_unit_cache::invalidate() { units = {}; lru_counter = 0; cache_miss_count = 0; } void texture_unit_cache::lock(size_t i, GL::AbstractTexture* tex) { fm_assert(i < unit_count); units[i] = { .ptr = tex, .lru_val = (uint64_t)-1, }; } void texture_unit_cache::unlock(size_t i, bool reuse_immediately) { fm_assert(i < unit_count); if (units[i].ptr == (GL::AbstractTexture*)-1) reuse_immediately = true; units[i] = { .ptr = units[i].ptr, .lru_val = reuse_immediately ? 0 : ++lru_counter }; } void texture_unit_cache::output_stats() { #if 0 using namespace std::literals; auto total = cache_hit_count + cache_miss_count; static auto t0 = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now(); if (t - t0 > 5s) [[unlikely]] { t0 = t; [[maybe_unused]] auto ratio = (double)cache_hit_count/(double)(cache_hit_count+cache_miss_count); printf("texture-binding: hit rate %.2f%% (%zu binds total)\n", ratio*100, total); std::fflush(stdout); } if (total > (size_t)1e4) { cache_hit_count /= 9; cache_miss_count /= 9; } #endif } size_t texture_unit_cache::get_unit_count() { static auto ret = [] { GLint value = 0; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &value); //Debug{} << "texture-binding: got" << value << "texture image units"; fm_assert(value >= /*GL 3.3*/ 16); //value = 16; // limit for performance testing return value; }(); return (size_t)ret; } int32_t texture_unit_cache::bind(GL::AbstractTexture& x) { return bind(&x); } } // namespace floormat