-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathproducerConsumerExample.cpp
292 lines (243 loc) · 8.48 KB
/
producerConsumerExample.cpp
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
// This file is autogenerated. See look at the .lzz files in the src/ directory for a more human-friendly version
// producerConsumerExample.hh
//
#ifndef LZZ_producerConsumerExample_hh
#define LZZ_producerConsumerExample_hh
// SdlStbFont example
// By Liam Twigger - 2020
// Public Domain
#define SDL_STB_FONT_IMPL
#define LZZ_INLINE inline
#undef LZZ_INLINE
#endif
////////////////////////////////////////////////////////////////////////
#ifdef SDL_STB_FONT_IMPL
#ifndef SDL_STB_FONT_IMPL_DOUBLE_GUARD_producerConsumerExample
#define SDL_STB_FONT_IMPL_DOUBLE_GUARD_producerConsumerExample
// producerConsumerExample.cpp
//
#include <string>
#include <vector>
#include <SDL2/SDL.h>
#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <atomic>
#include <sstream>
#define SDL_STB_FONT_IMPL
#include "sdlStbFont.h"
#include "producerConsumerFrontend.h"
/// Opens a file and returns a string of the raw data
void readFileRaw (const std::string & fullPath, std::string & output) {
std::ifstream fs(fullPath.c_str(), std::ios::in | std::ios::binary);
if (!fs.is_open()) {
std::cout << "readFileRaw: " << fullPath << " -- " << "WARNING: Could not open file." << std::endl;
exit(1);
return;
}
else {
std::cout << "Opened! " << fullPath << std::endl;
}
fs.seekg (0, std::ios::end);
const size_t LEN = fs.tellg();
fs.seekg (0, std::ios::beg);
output.resize(LEN);
fs.read(&output[0], LEN);
fs.close();
}
////////////////////////////////////////////////////////////////////////
// Example command buffer
#define COMMAND_TYPE_SUB_ID 0
#define COMMAND_TYPE_RECT 1
#define COMMAND_TYPE_PRETEXT 2
#define COMMAND_TYPE_TEXT 3
struct command {
int type;
pcfc_handle handle;
int x, y, w, h;
inline command (int _type, pcfc_handle _handle = -1, int _x = 0, int _y = 0, int _w = 0, int _h = 0) :
type(_type), handle(_handle), x(_x), y(_y), w(_w), h(_h) {}
};
class CommandBuffer {
public:
// Quick and dirty command buffer implementation
// Consumer will resuse its buffer if producer has not made any
// A better way to do this is to have a semaphore on the producer thread
// and have the consumer signal said semaphore when it consumes
std::vector<command> mCommandsProducer;
std::vector<command> mCommandsTx;
std::vector<command> mCommandsConsumer;
std::mutex mMutex;
std::atomic<int> workFlag;
void push_back(command c) {
mCommandsProducer.push_back(std::move(c));
}
void waitForWorkflag() {
while (workFlag > 0) {
std::this_thread::yield(); //spinlock until consumer thread is finished
}
}
void submitToConsumer() {
workFlag = 1; // indicate that we have fresh data
std::lock_guard LG(mMutex);
mCommandsTx.swap(mCommandsProducer);
}
void receiveFromProducer() {
workFlag = 0; // indicate that we are ready for stuff
std::lock_guard LG(mMutex);
if (mCommandsTx.size()) {
mCommandsConsumer.clear();
mCommandsTx.swap(mCommandsConsumer);
}
}
};
class pcfc_example {
public:
producer_consumer_font_cache mPcCache;
sdl_stb_font_cache mSdlFontCache;
CommandBuffer mCommandBuffer; // an example of this is at https://github.com/SnapperTT/nanovg_command_buffer
std::atomic<int> killFlag;
void initExample(std::string & fontData) {
mPcCache.consumer_font_cache = &mSdlFontCache;
killFlag = 0;
sttfont_memory m;
m.data = &fontData[0];
m.size = fontData.size();
m.ownsData = false;
mPcCache.faceSize = 24;
mPcCache.loadFontManagedBoth(m); // loads a copy of the font into both mPcCache and mRenderCache
// note: faceSize, etc are sync'ed between both copies in here
}
void producerExample() {
// example producer thread
// init
pcfc_prerendered_text prt;
mPcCache.renderTextToObject(&prt, "Prerendered text from Producer Thread!"); // prt now holds a handle
//mPcCache.submitToConsumer();
int submissionCounter = 0;
// main loop - buffer draw commands somehow
while (!killFlag) {
mCommandBuffer.push_back(command(COMMAND_TYPE_SUB_ID, submissionCounter));
mCommandBuffer.push_back(command(COMMAND_TYPE_RECT, -1, 0, 0, 450, 100));
mCommandBuffer.push_back(command(COMMAND_TYPE_PRETEXT, prt.handle, 5, 35));
std::stringstream ss;
ss << "Direct Text from Producer Thread, Submission id: " << submissionCounter;
pcfc_handle textHandle = mPcCache.pushText(5,5, ss.str());
mCommandBuffer.push_back(command(COMMAND_TYPE_TEXT, textHandle));
//if (someUpdate) {
// mPcCache.destoryPrerender(id);
// mPcCache.submitToConsumer();
// }
mCommandBuffer.waitForWorkflag(); // we have to wait otherwise we might override the frame in flight
mPcCache.submitToConsumer();
mCommandBuffer.submitToConsumer();
//SDL_Delay(20);
submissionCounter++;
}
std::cout << "Producer thread exited" << std::endl;
}
static void producer_thread_main(void* data) {
pcfc_example * I = (pcfc_example*) data;
I->producerExample();
}
void consumerExample() {
// example render thread
int frameCounter = 0;
int lastSubCounter = 0;
uint64_t NOW = SDL_GetPerformanceCounter();
uint64_t LAST = 0;
while (true) {
// Receive from producer thread
if (mCommandBuffer.workFlag) {
mPcCache.receiveFromProducer();
mPcCache.dispatchPrerenderJobs<sdl_stb_prerendered_text>(); // must be called to actually prerender the text
mCommandBuffer.receiveFromProducer();
}
// Handle SDL events
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_QUIT:
{
killFlag = 1;
mCommandBuffer.workFlag = 0;
SDL_Delay(10);
return;
break;
}
}
}
// Setup SDL Rendering
SDL_Renderer* mSdlRenderer = mSdlFontCache.mRenderer;
SDL_SetRenderDrawColor(mSdlRenderer, 125, 125, 125, 255); // Grey background to test glyph artefacts
SDL_RenderClear(mSdlRenderer);
// Process command buffer
for (const command & mCommand : mCommandBuffer.mCommandsConsumer) {
switch (mCommand.type) {
case COMMAND_TYPE_SUB_ID:
lastSubCounter = mCommand.handle;
continue;
case COMMAND_TYPE_PRETEXT:
mPcCache.dispatchSinglePrerendered(mCommand.handle, mCommand.x, mCommand.y);
continue;
case COMMAND_TYPE_TEXT:
mPcCache.dispatchSingleText(mCommand.handle);
continue;
case COMMAND_TYPE_RECT:
{
SDL_SetRenderDrawColor(mSdlRenderer, 55, 55, 55, 255); // dark grey rectangle
SDL_Rect r;
r.x = mCommand.x; r.y = mCommand.y; r.w = mCommand.w; r.h = mCommand.h;
SDL_RenderDrawRect(mSdlRenderer, &r);
continue;
}
default:
continue;
}
}
std::stringstream ss;
ss << "Text from consumer thread! Submission id: " << lastSubCounter
<< "\nframe counter: " << frameCounter
;
mSdlFontCache.drawText(5, 65, ss.str());
SDL_RenderPresent(mSdlRenderer);
frameCounter++;
//SDL_Delay(13);
if (frameCounter % 100 == 0) {
LAST = NOW;
NOW = SDL_GetPerformanceCounter();
const double deltaTime = (double)((NOW - LAST)*1000 / (double)SDL_GetPerformanceFrequency() ) / 100.0;
std::cout << "Producer Consumer - Consumer Thread - 100 Frames Average - Frametime: " << deltaTime << "ms \t FPS: " << (1000.0/deltaTime) << std::endl;
}
}
}
};
int main(int argc, char**argv) {
// Setup the SDL window & renderer
int windowWidth = 800;
int windowHeight = 1000;
SDL_Init(0);
SDL_Window * mWindow = SDL_CreateWindow("Producer Consumer Example Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
SDL_Renderer * mSdlRenderer = SDL_CreateRenderer(mWindow, SDL_RENDERER_SOFTWARE, 0);
pcfc_example ex;
ex.mSdlFontCache.bindRenderer(mSdlRenderer); // Must bind a renderer before generating any glyphs
{
std::string fontData;
readFileRaw("fonts/NotoSans-Regular.ttf", fontData);
ex.initExample(fontData);
}
// spawn a producer thread
std::thread producerThread(pcfc_example::producer_thread_main, &ex);
ex.consumerExample();
producerThread.join();
// Cleanup - just let ex.mPcCache fall out of scope
ex.mPcCache.freeStoredPrerenderedText(true); // deletes all prerendered text objects stored. true == also calls prt->freeTexture() for all prerendered text
// this is manual destruction as destroying a large number of objects can be expensive, esp. when you want to exit quickly
std::cout << "Consumer thread exited" << std::endl;
return 1;
}
#define LZZ_INLINE inline
#undef LZZ_INLINE
#endif //SDL_STB_FONT_IMPL_DOUBLE_GUARD_producerConsumerExample
#endif //SDL_STB_FONT_IMPL_IMPL