Skip to content

Commit 52c23a3

Browse files
committed
PenLayer: Add some rendering optimizations
1 parent d97a549 commit 52c23a3

File tree

2 files changed

+111
-51
lines changed

2 files changed

+111
-51
lines changed

src/penlayer.cpp

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,28 @@
44

55
#include "penlayer.h"
66
#include "penlayerpainter.h"
7-
#include "penattributes.h"
87
#include "irenderedtarget.h"
98
#include "spritemodel.h"
109
#include "stagemodel.h"
1110

1211
using namespace scratchcpprender;
1312

1413
static const double pi = std::acos(-1); // TODO: Use std::numbers::pi in C++20
14+
static const int PEN_LINES_RESERVE = 10240;
1515

1616
std::unordered_map<libscratchcpp::IEngine *, IPenLayer *> PenLayer::m_projectPenLayers;
1717

1818
PenLayer::PenLayer(QNanoQuickItem *parent) :
1919
IPenLayer(parent)
2020
{
2121
setSmooth(false);
22+
23+
// Reserve space for pen lines
24+
m_penLines.reserve(PEN_LINES_RESERVE);
25+
26+
for (int i = 0; i < PEN_LINES_RESERVE; i++) {
27+
m_penLines.push_back(PenLine());
28+
}
2229
}
2330

2431
PenLayer::~PenLayer()
@@ -119,14 +126,34 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine)
119126
void PenLayer::beginFrame()
120127
{
121128
m_fbo->bind();
122-
beginPainterFrame();
123-
m_frameChanged = false;
129+
m_glF->glDisable(GL_SCISSOR_TEST);
130+
m_glF->glDisable(GL_DEPTH_TEST);
131+
132+
m_glF->glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
133+
m_glF->glBindVertexArray(m_vao);
134+
135+
m_penLineAdded = false;
136+
m_stampAdded = false;
124137
}
125138

126139
void PenLayer::endFrame()
127140
{
141+
m_glF->glBindVertexArray(0);
142+
m_glF->glBindBuffer(GL_ARRAY_BUFFER, 0);
143+
144+
beginPainterFrame();
145+
renderLines();
128146
endPainterFrame();
147+
129148
m_fbo->release();
149+
m_glF->glEnable(GL_SCISSOR_TEST);
150+
m_glF->glEnable(GL_DEPTH_TEST);
151+
152+
if (m_penLineAdded || m_stampAdded) {
153+
update();
154+
m_penLineAdded = false;
155+
m_stampAdded = false;
156+
}
130157
}
131158

132159
bool PenLayer::hqPen() const
@@ -151,16 +178,12 @@ void scratchcpprender::PenLayer::clear()
151178

152179
Q_ASSERT(m_fbo->isBound());
153180

154-
if (m_frameChanged) {
155-
endPainterFrame();
156-
beginPainterFrame();
157-
m_frameChanged = false;
158-
}
181+
m_penLineCount = 0;
182+
m_penLineAdded = false;
183+
m_stampAdded = false;
159184

160-
m_glF->glDisable(GL_SCISSOR_TEST);
161185
m_glF->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
162186
m_glF->glClear(GL_COLOR_BUFFER_BIT);
163-
m_glF->glEnable(GL_SCISSOR_TEST);
164187

165188
m_textureDirty = true;
166189
m_boundsDirty = true;
@@ -193,33 +216,25 @@ void scratchcpprender::PenLayer::drawLine(const PenAttributes &penAttributes, do
193216
x1 += stageWidthHalf;
194217
y1 = stageHeightHalf - y1;
195218

196-
// Set pen attributes
197-
const double diameter = penAttributes.diameter * m_scale;
198-
m_painter->setLineWidth(diameter);
199-
m_painter->setStrokeStyle(penAttributes.color);
200-
m_painter->setFillStyle(penAttributes.color);
201-
m_painter->setLineJoin(QNanoPainter::JOIN_ROUND);
202-
m_painter->setLineCap(QNanoPainter::CAP_ROUND);
203-
m_painter->setAntialias(m_antialiasingEnabled ? 1.0f : 0.0f);
204-
m_painter->beginPath();
205-
206-
// Width 1 and 3 lines need to be offset by 0.5
207-
const double offset = (std::fmod(std::max(4 - diameter, 0.0), 2)) / 2;
219+
// Render the line later
220+
if (m_penLineCount == m_penLines.size()) {
221+
m_penLines.reserve(m_penLineCount * 2);
208222

209-
// If the start and end coordinates are the same, draw a point, otherwise draw a line
210-
if (x0 == x1 && y0 == y1) {
211-
m_painter->circle(x0 + offset, y0 + offset, diameter / 2);
212-
m_painter->fill();
213-
} else {
214-
m_painter->moveTo(x0 + offset, y0 + offset);
215-
m_painter->lineTo(x1 + offset, y1 + offset);
216-
m_painter->stroke();
223+
for (size_t i = m_penLineCount; i < m_penLineCount * 2; i++) {
224+
m_penLines.push_back(PenLine());
225+
}
217226
}
218227

228+
PenLine &line = m_penLines[m_penLineCount++];
229+
line.x0 = x0;
230+
line.y0 = y0;
231+
line.x1 = x1;
232+
line.y1 = y1;
233+
line.attributes = penAttributes;
234+
219235
m_textureDirty = true;
220236
m_boundsDirty = true;
221-
m_frameChanged = true;
222-
update();
237+
m_penLineAdded = true;
223238
}
224239

225240
void PenLayer::stamp(IRenderedTarget *target)
@@ -229,31 +244,24 @@ void PenLayer::stamp(IRenderedTarget *target)
229244

230245
Q_ASSERT(m_fbo->isBound());
231246

232-
if (m_frameChanged)
233-
endPainterFrame();
247+
if (m_penLineAdded) {
248+
m_glF->glBindVertexArray(0);
249+
m_glF->glBindBuffer(GL_ARRAY_BUFFER, 0);
234250

235-
m_glF->glDisable(GL_SCISSOR_TEST);
236-
m_glF->glDisable(GL_DEPTH_TEST);
251+
beginPainterFrame();
252+
renderLines();
253+
endPainterFrame();
254+
m_penLineAdded = false;
237255

238-
m_glF->glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
239-
m_glF->glBindVertexArray(m_vao);
256+
m_glF->glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
257+
m_glF->glBindVertexArray(m_vao);
258+
}
240259

241260
target->render(m_scale);
242261

243-
m_glF->glBindVertexArray(0);
244-
m_glF->glBindBuffer(GL_ARRAY_BUFFER, 0);
245-
246-
m_glF->glEnable(GL_SCISSOR_TEST);
247-
m_glF->glEnable(GL_DEPTH_TEST);
248-
249-
if (m_frameChanged) {
250-
beginPainterFrame();
251-
m_frameChanged = false;
252-
}
253-
254262
m_textureDirty = true;
255263
m_boundsDirty = true;
256-
update();
264+
m_stampAdded = true;
257265
}
258266

259267
void PenLayer::refresh()
@@ -451,3 +459,38 @@ void PenLayer::updateTexture()
451459
m_textureDirty = false;
452460
m_textureManager.removeTexture(m_texture);
453461
}
462+
463+
void PenLayer::renderLines()
464+
{
465+
for (size_t i = 0; i < m_penLineCount; i++) {
466+
renderLine(m_penLines[i]);
467+
}
468+
469+
m_penLineCount = 0;
470+
}
471+
472+
void PenLayer::renderLine(const PenLine &line)
473+
{
474+
// Set pen attributes
475+
const double diameter = line.attributes.diameter * m_scale;
476+
m_painter->setLineWidth(diameter);
477+
m_painter->setStrokeStyle(line.attributes.color);
478+
m_painter->setFillStyle(line.attributes.color);
479+
m_painter->setLineJoin(QNanoPainter::JOIN_ROUND);
480+
m_painter->setLineCap(QNanoPainter::CAP_ROUND);
481+
m_painter->setAntialias(m_antialiasingEnabled ? 1.0f : 0.0f);
482+
m_painter->beginPath();
483+
484+
// Width 1 and 3 lines need to be offset by 0.5
485+
const double offset = (std::fmod(std::max(4 - diameter, 0.0), 2)) / 2;
486+
487+
// If the start and end coordinates are the same, draw a point, otherwise draw a line
488+
if (line.x0 == line.x1 && line.y0 == line.y1) {
489+
m_painter->circle(line.x0 + offset, line.y0 + offset, diameter / 2);
490+
m_painter->fill();
491+
} else {
492+
m_painter->moveTo(line.x0 + offset, line.y0 + offset);
493+
m_painter->lineTo(line.x1 + offset, line.y1 + offset);
494+
m_painter->stroke();
495+
}
496+
}

src/penlayer.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "ipenlayer.h"
1111
#include "texture.h"
1212
#include "cputexturemanager.h"
13+
#include "penattributes.h"
1314

1415
namespace scratchcpprender
1516
{
@@ -64,10 +65,22 @@ class PenLayer : public IPenLayer
6465
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
6566

6667
private:
68+
struct PenLine
69+
{
70+
double x0;
71+
double y0;
72+
double x1;
73+
double y1;
74+
PenAttributes attributes;
75+
};
76+
6777
void beginPainterFrame();
6878
void endPainterFrame();
6979
void updateTexture();
7080

81+
void renderLines();
82+
void renderLine(const PenLine &line);
83+
7184
static std::unordered_map<libscratchcpp::IEngine *, IPenLayer *> m_projectPenLayers;
7285
bool m_antialiasingEnabled = true;
7386
libscratchcpp::IEngine *m_engine = nullptr;
@@ -85,7 +98,11 @@ class PenLayer : public IPenLayer
8598
mutable libscratchcpp::Rect m_bounds;
8699
GLuint m_vbo = 0;
87100
GLuint m_vao = 0;
88-
bool m_frameChanged = false;
101+
102+
bool m_penLineAdded = false;
103+
bool m_stampAdded = false;
104+
std::vector<PenLine> m_penLines;
105+
size_t m_penLineCount = 0;
89106
};
90107

91108
} // namespace scratchcpprender

0 commit comments

Comments
 (0)