1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
#include "texture-unit-cache.hpp"
#include "compat/assert.hpp"
#include <cstdio>
#include <Corrade/Containers/String.h>
#include <Magnum/GL/Texture.h>
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;
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(floormat::size_t i, GL::AbstractTexture* tex)
{
fm_assert(i < unit_count);
units[i] = { .ptr = tex, .lru_val = (uint64_t)i, };
}
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()
{
auto total = cache_hit_count + cache_miss_count;
if (total > 0)
{
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, (size_t)total); std::fflush(stdout);
}
if (total > (size_t)10'000)
{
cache_hit_count /= 5;
cache_miss_count /= 5;
}
}
size_t texture_unit_cache::get_unit_count()
{
static auto ret = [] {
GLint value = 0;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &value);
fm_assert(value >= /*GL 3.3*/ 16);
//value = 1; // limit for performance testing
return value;
}();
return (size_t)ret;
}
int32_t texture_unit_cache::bind(GL::AbstractTexture& x) { return bind(&x); }
} // namespace floormat
|