-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupdateoptimizer.cpp
More file actions
184 lines (166 loc) · 4.89 KB
/
updateoptimizer.cpp
File metadata and controls
184 lines (166 loc) · 4.89 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include <iostream>
#include <cstring>
#include "updateoptimizer.hpp"
#include "display.hpp"
namespace
{
#ifdef OPTIMIZE_TILE_UPDATES
size_t DetermineTileSize(size_t width, size_t height)
{
if ((width % 16) == 0 && (height % 16) == 0)
{
return 16;
}
if ((width % 8) == 0 && (height % 8) == 0)
{
return 8;
}
return 4;
}
#endif
}
UpdateOptimizer::UpdateOptimizer(size_t width, size_t height)
: m_width(width)
, m_height(height)
{
#ifdef OPTIMIZE_TILE_UPDATES
const size_t bufferSize{width * height};
m_prevFrameBuffer = new uint16_t[bufferSize];
memset(m_prevFrameBuffer, 0, bufferSize * sizeof(uint16_t));
m_tileSize = DetermineTileSize(width, height);
m_tileWidth = width / m_tileSize;
m_tileHeight = height / m_tileSize;
m_tileChanged = new uint8_t[m_tileWidth * m_tileHeight];
std::cout << "Tile dimensions (" << m_tileSize << ", " << m_tileWidth << ", " << m_tileHeight << ")" << std::endl;
#endif
}
// The algorithm is to break up the screen into square shaped tiles of a fixed size. We use a 16x16 tile
// size if it fits the resolution, otherwise we step down to 8x8 then 4 as the fall-back. We then compare
// each tile and mark if it has been updated. For an 800x480 LCD, we have 50x30 tiles. Updated tiles are
// then added to a list, with ajoining tiles being merged into a single larger tile.
void UpdateOptimizer::TransferToDisplay(uint16_t* frameBuffer, Display& display)
{
#ifdef OPTIMIZE_TILE_UPDATES
if (display.MustBlitFullScreen())
{
const size_t length = m_width * m_height * sizeof(uint16_t);
display.Blit(0, m_width, 0, m_height, frameBuffer, length);
}
else
{
CreateUpdatedTileTable(frameBuffer);
CollectChangedTiles();
BlitChangedTilesToDisplay(frameBuffer, display);
memcpy(m_prevFrameBuffer, frameBuffer, m_width * m_height * sizeof(uint16_t));
}
#else
const size_t length = m_width * m_height * sizeof(uint16_t);
display.Blit(0, m_width, 0, m_height, frameBuffer, length);
#endif
}
#ifdef OPTIMIZE_TILE_UPDATES
void UpdateOptimizer::CreateUpdatedTileTable(uint16_t* frameBuffer)
{
memset(m_tileChanged, 0, m_tileWidth * m_tileHeight * sizeof(m_tileChanged[0]));
const size_t numPixels = m_width * m_height;
for (size_t p = 0; p < numPixels; ++p)
{
if (frameBuffer[p] != m_prevFrameBuffer[p])
{
MarkTileChanged(p);
}
}
DumpTileState();
}
void UpdateOptimizer::MarkTileChanged(size_t pixel)
{
const size_t tileY = (pixel / m_width) / m_tileSize;
const size_t tileX = (pixel % m_width) / m_tileSize;
m_tileChanged[tileY * m_tileWidth + tileX] = 1;
}
bool UpdateOptimizer::IsTileChanged(size_t tile)
{
return m_tileChanged[tile] > 0;
}
void UpdateOptimizer::CollectChangedTiles()
{
m_rectList.clear();
const size_t numTiles = m_tileWidth * m_tileHeight;
for (size_t t = 0; t < numTiles; ++t)
{
if (IsTileChanged(t))
{
MergeChangedTile(t);
}
}
}
void UpdateOptimizer::MergeChangedTile(size_t tile)
{
const size_t y = tile / m_tileWidth;
const size_t x = tile % m_tileWidth;
const size_t size = m_tileSize;
Rect r1{ x * size, x * size + size, y * size, y * size + size };
#ifdef GROUP_CHANGED_TILES
bool updated;
do
{
updated = false;
for (auto iter = m_rectList.begin(); iter != m_rectList.end(); ++iter)
{
if (r1.Intersects(*iter))
{
r1.Union(*iter);
updated = true;
m_rectList.erase(iter);
break;
}
}
} while (updated);
#endif
m_rectList.push_back(r1);
}
void UpdateOptimizer::BlitChangedTilesToDisplay(uint16_t* frameBuffer, Display& display)
{
for (Rect r : m_rectList)
{
uint16_t* data = m_prevFrameBuffer;
const size_t dataSize = GetRectDataSize(r);
GatherRectData(r, data, frameBuffer);
display.Blit(r.x0, r.x1, r.y0, r.y1, data, dataSize);
std::cout << "Rect(" << r.x0 << "," << r.x1 << "," << r.y0 << "," << r.y1 << ") size " << dataSize << std::endl;
}
}
size_t UpdateOptimizer::GetRectDataSize(const Rect& r)
{
return r.GetNumPixels() * sizeof(uint16_t);
}
void UpdateOptimizer::GatherRectData(const Rect& r, uint16_t* buffer, uint16_t* frameBuffer)
{
for (size_t row = r.y0; row < r.y1; ++row)
{
for (size_t col = r.x0; col < r.x1; ++col)
{
*buffer++ = frameBuffer[row * m_width + col];
}
}
}
void UpdateOptimizer::DumpTileState()
{
const size_t numTiles = m_tileWidth * m_tileHeight;
for (size_t t = 0; t < numTiles; ++t)
{
if (IsTileChanged(t))
{
std::cout << "*";
}
else
{
std::cout << ".";
}
if (((t + 1) % m_tileWidth) == 0)
{
std::cout << std::endl;
}
}
}
#endif