summaryrefslogtreecommitdiffhomepage
path: root/shaders/texture-unit-cache.cpp
blob: ec4a2212de7d478fa25266c629908e48617abbba (plain)
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
125
126
#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;
            //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(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)
        {
            [[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, (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);
        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