Loading...
Searching...
No Matches
QImagePool.hpp
1#pragma once
2#include <score/tools/Debug.hpp>
3
4#include <ossia/detail/hash.hpp>
5#include <ossia/detail/hash_map.hpp>
6#include <ossia/detail/mutex.hpp>
7
8#include <QDebug>
9#include <QImage>
10#include <QVector>
11
12#include <chrono>
13#include <mutex>
14
15namespace std
16{
17template <>
18class hash<std::pair<int, int>>
19{
20public:
21 std::size_t operator()(const std::pair<int, int>& p) const
22 {
23 std::size_t seed = 0;
24 ossia::hash_combine(seed, p.first);
25 ossia::hash_combine(seed, p.second);
26 return seed;
27 }
28};
29}
30
31namespace Media::Sound
32{
34{
35 struct Images
36 {
37 QVector<QImage*> images;
38 std::chrono::steady_clock::time_point last_touched;
39 };
40
41 static const constexpr int max_count = 100;
42 using pool_t = ossia::hash_map<std::pair<int, int>, Images>;
43 pool_t pool TS_GUARDED_BY(m_mtx);
44 std::mutex m_mtx;
45
46 static QImagePool& instance() noexcept
47 {
48 static QImagePool pool;
49 return pool;
50 }
51
53 {
54 for(auto& pair : pool)
55 {
56 for(QImage* img : pair.second.images)
57 {
58 delete img;
59 }
60 }
61 }
62
63 static inline int hit = 0;
64 static inline int miss = 0;
65 QImage* request(int w, int h)
66 {
67 std::lock_guard _{m_mtx};
68 // cacheStats();
69 auto it = pool.find({w, h});
70 if(it != pool.end())
71 {
72 auto& vec = it->second.images;
73 if(!vec.empty())
74 {
75 auto img = vec.front();
76 vec.pop_front();
77 img->fill(Qt::transparent);
78 it->second.last_touched = std::chrono::steady_clock::now();
79 hit++;
80 return img;
81 }
82 }
83
84 auto img = new QImage(w, h, QImage::Format_ARGB32_Premultiplied);
85 img->fill(Qt::transparent);
86 miss++;
87 return img;
88 }
89
90 void giveBack(const QVector<QImage*>& imgs)
91 {
92 for(auto img : imgs)
93 {
94 QVector<QImage*> to_delete;
95
96 {
97 std::lock_guard _{m_mtx};
98 to_delete = gc();
99 Images& images = pool[std::make_pair(img->width(), img->height())];
100 SCORE_ASSERT(!images.images.contains(img));
101 images.images.push_back(img);
102 images.last_touched = std::chrono::steady_clock::now();
103 }
104
105 for(auto img : to_delete)
106 delete img;
107 }
108 }
109
110 QVector<QImage*> gc()
111 {
112 if(pool.empty())
113 return {};
114
115 int count = 0;
116 auto oldest = pool.begin();
117 auto oldest_t = oldest->second.last_touched;
118 for(auto it = oldest; it != pool.end(); ++it)
119 {
120 if(it->second.last_touched < oldest->second.last_touched)
121 {
122 oldest = it;
123 oldest_t = it->second.last_touched;
124 }
125 count += it->second.images.size();
126 }
127
128 if(count < max_count)
129 return {};
130 auto res = std::move(oldest->second.images);
131 SCORE_ASSERT(oldest->second.images.isEmpty());
132 oldest->second.last_touched = std::chrono::steady_clock::now();
133 return res;
134 }
135
136 void cacheStats()
137 {
138 std::size_t bytes = 0;
139 int images = 0;
140 for(auto& pair : pool)
141 {
142 for(QImage* img : pair.second.images)
143 {
144 bytes += img->sizeInBytes();
145 images++;
146 }
147 }
148
149 qDebug() << QString("%1 images: %2 megabytes ; hit/miss ratio : %3 / %4 = %5")
150 .arg(images)
151 .arg(bytes / (1024 * 1024))
152 .arg(hit)
153 .arg(miss)
154 .arg(double(hit) / miss);
155 }
156};
157}
STL namespace.
Definition QImagePool.hpp:36
Definition QImagePool.hpp:34