summaryrefslogtreecommitdiffhomepage
path: root/shaders/texture-unit-cache.cpp
blob: 4c261b41564586a89a31fe3669fb64199cc3d60f (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
#include "texture-unit-cache.hpp"

#include "compat/assert.hpp"
#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);
        ++rebind_count;
        units[min_index] = {tex, ++lru_counter};
        tex->bind((Int)min_index);
        Debug{Debug::Flag::NoSpace} << "binding '" << 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;
    rebind_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 immediately)
{
    fm_assert(i < unit_count);
    if (units[i].ptr == (GL::AbstractTexture*)-1)
        immediately = true;
    units[i] = { .ptr = units[i].ptr, .lru_val = immediately ? 0 : ++lru_counter };
}

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);
        return value;
    }();
    return (size_t)ret;
}

int32_t texture_unit_cache::bind(GL::AbstractTexture& x) { return bind(&x); }

} // namespace floormat