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
|
// SPDX-License-Identifier: GPL-2.0-or-later
#include <unordered_map>
#include <vector>
#include <cassert>
#include <boost/unordered_map.hpp> // For hash of pair
#include "helper/mathfns.h"
#include "texturecache.h"
namespace Inkscape {
namespace UI {
namespace Widget {
namespace {
class BasicTextureCache : public TextureCache
{
static int constexpr min_dimension = 16;
static int constexpr expiration_timeout = 10000;
static int constexpr dim_to_ind(int dim) { return Util::floorlog2((dim - 1) / min_dimension) + 1; }
static int constexpr ind_to_maxdim(int index) { return min_dimension * (1 << index); }
static std::pair<int, int> dims_to_inds(Geom::IntPoint const &dims) { return { dim_to_ind(dims.x()), dim_to_ind(dims.y()) }; }
static Geom::IntPoint inds_to_maxdims(std::pair<int, int> const &inds) { return { ind_to_maxdim(inds.first), ind_to_maxdim(inds.second) }; }
// A cache of POT textures.
struct Bucket
{
std::vector<Texture> unused;
int used = 0;
int high_use_count = 0;
};
boost::unordered_map<std::pair<int, int>, Bucket> buckets;
// Used to periodicially discard excess cached textures.
int expiration_timer = 0;
public:
Texture request(Geom::IntPoint const &dimensions) override
{
// Find the bucket that the dimensions fall into.
auto indexes = dims_to_inds(dimensions);
auto &b = buckets[indexes];
// Reuse or create a texture of the appropriate dimensions.
Texture tex;
if (!b.unused.empty()) {
tex = std::move(b.unused.back());
b.unused.pop_back();
glBindTexture(GL_TEXTURE_2D, tex.id());
} else {
tex = Texture(inds_to_maxdims(indexes)); // binds
}
// Record the new use count of the bucket.
b.used++;
if (b.used > b.high_use_count) {
// If the use count has gone above the high-water mark, record this, and reset the timer for when to clean up excess unused textures.
b.high_use_count = b.used;
expiration_timer = 0;
}
return tex;
}
void finish(Texture tex) override
{
auto indexes = dims_to_inds(tex.size());
auto &b = buckets[indexes];
// Orphan the texture, if possible.
tex.invalidate();
// Put the texture back in its corresponding bucket's cache of unused textures.
b.unused.emplace_back(std::move(tex));
b.used--;
// If the expiration timeout has been reached, prune the cache of textures down to what was actually used in the last cycle.
expiration_timer++;
if (expiration_timer >= expiration_timeout) {
expiration_timer = 0;
for (auto &[k, b] : buckets) {
int max_unused = b.high_use_count - b.used;
assert(max_unused >= 0);
if (b.unused.size() > max_unused) {
b.unused.resize(max_unused);
}
b.high_use_count = b.used;
}
}
}
};
} // namespace
std::unique_ptr<TextureCache> TextureCache::create()
{
return std::make_unique<BasicTextureCache>();
}
} // namespace Widget
} // namespace UI
} // namespace Inkscape
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
|