libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
GuiMeshCache.cpp
1/****************************************************************************
2 * libgui
3 * Daryl Borth 2026
4 * GuiMeshCache.cpp
5 ***************************************************************************/
6
7#include "Gui.h"
8#include "../drivers/Mutex.h"
9
10namespace {
11
12//Meshes no shape holds are kept for the next shape that wants one, the longest idle going first
13//once they add up to this many vertices (16 bytes each, plus the backend's copy). A keyboard
14//uses about nine distinct meshes of a thousand or so vertices
15const size_t kMaxIdleVertices = 32768;
16
17//A mesh is handed to the backend the second time it is drawn, so a shape that changes every
18//frame (a progress bar filling up) never pays for a backend copy it won't reuse
19const uint32_t kUsesBeforeHandle = 2;
20
21std::vector<GuiSharedMesh *> entries;
22size_t idleVertices = 0;
23uint32_t clockTick = 0;
24Mutex * lockPtr = nullptr;
25
26Mutex& cacheLock()
27{
28 // Created on first use, which is the first shape the menu draws, on the draw thread
29 if(!lockPtr)
30 lockPtr = new Mutex();
31 return *lockPtr;
32}
33
34void destroyEntry(GuiSharedMesh * m, void * handle)
35{
36 if(handle)
37 {
38 VideoDriver * video = platform ? platform->getVideo() : nullptr;
39 if(video && video->getImageRenderer())
40 video->getImageRenderer()->destroyMesh(handle);
41 }
42 delete m;
43}
44
45} // namespace
46
47bool GuiMeshKey::operator==(const GuiMeshKey& o) const
48{
49 return kind == o.kind && width == o.width && height == o.height && feather == o.feather &&
50 tolerance == o.tolerance && corners.tl == o.corners.tl && corners.tr == o.corners.tr &&
51 corners.br == o.corners.br && corners.bl == o.corners.bl && paint == o.paint;
52}
53
54GuiSharedMesh::GuiSharedMesh(const GuiMeshKey& k) :
55 key(k), mesh(k.feather, k.tolerance), handle(nullptr), noHandle(false), uses(0), refs(0), idleStamp(0)
56{
57 GuiRect r = { 0.0f, 0.0f, (float)k.width, (float)k.height };
58 GuiAddPaintedShape(mesh, k.kind, r, k.corners, k.paint);
59}
60
62{
63 MutexLock lock(cacheLock());
64
65 for(size_t i = 0; i < entries.size(); i++)
66 {
67 GuiSharedMesh * m = entries[i];
68 if(m->key == key)
69 {
70 if(m->refs == 0)
71 idleVertices -= m->mesh.size();
72 m->refs++;
73 return m;
74 }
75 }
76
77 GuiSharedMesh * m = new GuiSharedMesh(key);
78 m->refs = 1;
79 entries.push_back(m);
80 return m;
81}
82
84{
85 if(!m)
86 return;
87
88 std::vector<std::pair<GuiSharedMesh *, void *> > evicted;
89 {
90 MutexLock lock(cacheLock());
91
92 if(--m->refs > 0)
93 return;
94
95 m->idleStamp = ++clockTick;
96 idleVertices += m->mesh.size();
97
98 // over budget: drop the meshes that have been idle longest
99 while(idleVertices > kMaxIdleVertices)
100 {
101 size_t oldest = entries.size();
102 for(size_t i = 0; i < entries.size(); i++)
103 {
104 if(entries[i]->refs == 0 && (oldest == entries.size() || entries[i]->idleStamp < entries[oldest]->idleStamp))
105 oldest = i;
106 }
107 if(oldest == entries.size())
108 break;
109 idleVertices -= entries[oldest]->mesh.size();
110 evicted.push_back(std::make_pair(entries[oldest], entries[oldest]->handle));
111 entries.erase(entries.begin() + oldest);
112 }
113 }
114
115 // the backend is asked to free its copies outside the lock
116 for(size_t i = 0; i < evicted.size(); i++)
117 destroyEntry(evicted[i].first, evicted[i].second);
118}
119
121{
122 std::vector<std::pair<GuiSharedMesh *, void *> > freed;
123 {
124 MutexLock lock(cacheLock());
125
126 for(size_t i = 0; i < entries.size();)
127 {
128 if(entries[i]->refs == 0)
129 {
130 freed.push_back(std::make_pair(entries[i], entries[i]->handle));
131 entries.erase(entries.begin() + i);
132 }
133 else
134 i++;
135 }
136 idleVertices = 0;
137 }
138
139 for(size_t i = 0; i < freed.size(); i++)
140 destroyEntry(freed[i].first, freed[i].second);
141}
142
143void GuiMeshCache::draw(GuiSharedMesh * m, float x, float y, float scaleX, float scaleY, uint8_t alpha, std::vector<MeshVertex>& scratch)
144{
145 if(!m)
146 return;
147 const std::vector<MeshVertex>& src = m->mesh.vertices();
148 if(src.empty())
149 return;
150
151 ImageRenderer * images = platform->getVideo()->getImageRenderer();
152
153 m->uses++;
154 if(!m->handle && !m->noHandle && m->uses >= kUsesBeforeHandle)
155 {
156 m->handle = images->createMesh(src.data(), src.size());
157 if(!m->handle)
158 m->noHandle = true;
159 }
160
161 // The backend's copy has its vertex colors baked in, so only a fully opaque draw can use it.
162 // A shape that is fading (an effect, a window sliding in) takes the path that rewrites alpha.
163 if(m->handle && alpha == 255)
164 {
165 images->drawMeshInstance(m->handle, x, y, scaleX, scaleY);
166 return;
167 }
168
169 scratch.resize(src.size());
170 for(size_t i = 0; i < src.size(); i++)
171 {
172 scratch[i].x = x + src[i].x * scaleX;
173 scratch[i].y = y + src[i].y * scaleY;
174 scratch[i].c = src[i].c;
175 if(alpha < 255)
176 scratch[i].c.a = (uint8_t)((src[i].c.a * alpha + 127) / 255);
177 }
178 images->drawMesh(scratch.data(), scratch.size());
179}
Umbrella header for the platform-agnostic libgui core.
static void draw(GuiSharedMesh *mesh, float x, float y, float scaleX, float scaleY, uint8_t alpha, std::vector< MeshVertex > &scratch)
Draws a mesh with its (0,0) at (x, y), stretched by scaleX/scaleY about that point.
static GuiSharedMesh * acquire(const GuiMeshKey &key)
Finds or builds the mesh for a key and takes a reference to it. Pair with release().
static void flush()
Frees every mesh nothing is holding, including the backend's copies (call when leaving the menu to gi...
static void release(GuiSharedMesh *mesh)
Gives back a reference from acquire().
size_t size() const
Gets the number of vertices (a multiple of 3)
const std::vector< MeshVertex > & vertices() const
Gets the triangle list.
One cached mesh.
Platform image/texture backend GuiImageData and GuiImage delegate to.
Definition VideoDriver.h:72
virtual void destroyMesh(void *mesh)
Releases a mesh from createMesh().
virtual void drawMeshInstance(void *mesh, float x, float y, float scaleX, float scaleY)
Draws a mesh from createMesh() fully opaque, as its vertex colors say.
virtual void * createMesh(const MeshVertex *vertices, size_t count)
Hands a triangle list to the backend to keep in its own ready-to-draw form (a GX display list,...
virtual void drawMesh(const MeshVertex *vertices, size_t count)=0
Draws a triangle list: three vertices per triangle, each with its own color.
RAII lock guard - locks on construction, unlocks on destruction.
Definition Mutex.h:37
A simple mutual-exclusion lock.
Definition Mutex.h:14
Platform video backend owning the frame lifecycle (init/render/clear, screen size,...
Definition VideoDriver.h:28
virtual ImageRenderer * getImageRenderer()=0
void GuiAddPaintedShape(GuiMesh &mesh, SHAPE shape, const GuiRect &r, const GuiCorners &corners, const GuiPaint &paint)
Adds a painted shape to a mesh: shadow first, then fill, then outline.
Definition GuiPaint.cpp:218
float tl
Top-left radius.
Definition GuiGeometry.h:41
float bl
Bottom-left radius.
Definition GuiGeometry.h:44
float tr
Top-right radius.
Definition GuiGeometry.h:42
float br
Bottom-right radius.
Definition GuiGeometry.h:43
Everything that decides what a shape's triangles are.
float tolerance
Curve tolerance, see GuiMesh.
int width
Width in design-canvas pixels.
GuiCorners corners
Corner radii of a rectangle.
GuiPaint paint
Fill, outline and shadow.
SHAPE kind
Rectangle or ellipse.
float feather
Anti-aliasing fringe width, see GuiMesh.
int height
Height in design-canvas pixels.
An axis-aligned rectangle (top-left origin) in design-canvas pixels.
Definition GuiGeometry.h:30