Skip to content

Commit d501660

Browse files
committed
Refactor draw iteration to prepare for batching
1 parent a9c80f5 commit d501660

1 file changed

Lines changed: 56 additions & 51 deletions

File tree

src/draw.ts

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ const MAX_QUEUE_SIZE = 1e6
3232
const MIN_PATTERN_SIZE = 0.0005 // ignore patterns where either side is smaller than this
3333
const DEBUG = true as boolean
3434

35-
// -- hacky global state
36-
37-
type GlobalMutableState = {
38-
drawScreenCalls: number
39-
maxQueueSize: number
40-
queueIterations: number
41-
}
42-
4335
// -- helper functions
4436

4537
const mapPatternToViewportSpace = (pattern: AbsolutePattern, screenSize: Size): ViewportPattern => ({
@@ -122,79 +114,92 @@ type QueueEntry = {
122114
depth: number
123115
}
124116

125-
const draw = (
126-
ctx: CanvasRenderingContext2D,
127-
state: State,
128-
globalMutableState: GlobalMutableState,
129-
queue: Queue<QueueEntry>
130-
): void => {
131-
const screenSize = getScreenSize(ctx)
117+
function* generateDrawQueue(state: State): Generator<QueueEntry, void, void> {
118+
console.log(
119+
`generateDrawQueue start. Initial screens: ${state.screens.length}, Patterns: ${state.patterns.length}`
120+
)
132121

133-
// Shared styles.
134-
ctx.fillStyle = 'black'
135-
ctx.lineWidth = 1
122+
const patternQueue = new Queue<QueueEntry>(
123+
state.screens.map(screen => ({
124+
currentPattern: screen,
125+
depth: 0,
126+
}))
127+
)
128+
129+
let iterations = 0
130+
while (patternQueue.size > 0) {
131+
iterations += 1
136132

137-
while (queue.size > 0) {
138-
globalMutableState.queueIterations += 1
139-
const { currentPattern, depth } = queue.shift()!
133+
const entry = patternQueue.shift()
140134

141-
if (depth > MAX_DEPTH) break
142-
// Always render to MIN_DEPTH even if the draw call budget is empty
143-
if (depth > MIN_DEPTH && globalMutableState.drawScreenCalls >= MAX_DRAW_CALLS) break
135+
yield entry
144136

145-
globalMutableState.drawScreenCalls += 1
146-
drawScreen(ctx, screenSize, currentPattern, COLORS[Math.min(COLORS.length - 1, depth)]!)
137+
// Don't add patterns that are too deep.
138+
if (entry.depth >= MAX_DEPTH) {
139+
console.log(`MAX_DEPTH (${MAX_DEPTH}) reached at depth ${entry.depth}. Breaking from generateDrawQueue loop.`)
140+
break
141+
}
147142

148143
for (const pattern of state.patterns) {
149-
const virtualScreen = combinePatterns(currentPattern, pattern)
144+
const virtualScreen = combinePatterns(entry.currentPattern, pattern)
150145

151146
if (isValidPattern(virtualScreen)) {
152-
queue.push({
147+
patternQueue.push({
153148
currentPattern: virtualScreen,
154-
depth: depth + 1,
149+
depth: entry.depth + 1,
155150
})
156151
}
157152
}
158153

159-
globalMutableState.maxQueueSize = Math.max(globalMutableState.maxQueueSize, queue.size)
154+
// Give up if queue becomes too large.
155+
if (patternQueue.size > MAX_QUEUE_SIZE) {
156+
console.warn('Maximum queue size reached. Rendering cancelled.')
157+
return
158+
}
160159
}
160+
161+
console.log(`generateDrawQueue done. Total iterations: ${iterations}. Final queue size: ${patternQueue.size}`)
161162
}
162163

163164
export function* drawFrameIncrementally(
164165
ctx: CanvasRenderingContext2D,
165166
state: State
166167
): Generator<void, void, void> {
167168
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
169+
ctx.fillStyle = 'black'
170+
ctx.lineWidth = 1
171+
172+
const screenSize = getScreenSize(ctx)
168173

169-
const drawQueue = new Queue(
170-
state.screens.map(screen => ({
171-
currentPattern: screen,
172-
depth: 0,
173-
}))
174-
)
174+
const drawQueueIterator = generateDrawQueue(state)
175175

176-
while (drawQueue.size > 0) {
177-
const globalMutableState: GlobalMutableState = {
178-
drawScreenCalls: 0,
179-
maxQueueSize: drawQueue.size,
180-
queueIterations: 0,
181-
}
176+
while (true) {
177+
let drawScreenCalls = 0
182178

183179
const duration = measure(() => {
184-
draw(ctx, state, globalMutableState, drawQueue)
185-
drawQueue.compact()
180+
// Manual iteration
181+
while (true) {
182+
const iteratorResult = drawQueueIterator.next()
183+
184+
if (iteratorResult.done) break
185+
186+
const { currentPattern, depth } = iteratorResult.value
187+
188+
drawScreenCalls += 1
189+
190+
drawScreen(ctx, screenSize, currentPattern, COLORS[Math.min(COLORS.length - 1, depth)]!)
191+
192+
if (depth > MIN_DEPTH && drawScreenCalls >= MAX_DRAW_CALLS) break
193+
}
186194
})
187195

188196
if (DEBUG) {
189-
console.log(
190-
`drawFrame done in ${duration.toFixed(0)}ms. Queue size: ${drawQueue.size}. ${JSON.stringify(globalMutableState)}.`
191-
)
197+
console.log(`drawFrame done in ${duration.toFixed(0)}ms. Drew ${drawScreenCalls} screens.`)
192198
}
193199

194-
// Give up if queue becomes too large.
195-
if (drawQueue.size > MAX_QUEUE_SIZE) {
196-
console.warn('Maximum queue size reached. Rendering cancelled.')
197-
return
200+
if (drawScreenCalls === 0) {
201+
console.log('No screens to draw. Exiting.')
202+
break
198203
}
199204

200205
yield

0 commit comments

Comments
 (0)