@swf2es/render 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +17 -2
- package/dist/commands.d.ts +67 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +93 -0
- package/dist/commands.js.map +1 -0
- package/dist/filter-plan.d.ts +110 -0
- package/dist/filter-plan.d.ts.map +1 -0
- package/dist/filter-plan.js +480 -0
- package/dist/filter-plan.js.map +1 -0
- package/dist/filter-shaders.d.ts +63 -0
- package/dist/filter-shaders.d.ts.map +1 -0
- package/dist/filter-shaders.js +350 -0
- package/dist/filter-shaders.js.map +1 -0
- package/dist/filters.d.ts +107 -0
- package/dist/filters.d.ts.map +1 -0
- package/dist/filters.js +187 -0
- package/dist/filters.js.map +1 -0
- package/dist/geometry.d.ts +83 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +93 -0
- package/dist/geometry.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/keeps.d.ts +17 -0
- package/dist/keeps.d.ts.map +1 -0
- package/dist/keeps.js +17 -0
- package/dist/keeps.js.map +1 -0
- package/dist/kernel-shaders.d.ts +74 -0
- package/dist/kernel-shaders.d.ts.map +1 -0
- package/dist/kernel-shaders.js +323 -0
- package/dist/kernel-shaders.js.map +1 -0
- package/dist/measure.d.ts +56 -0
- package/dist/measure.d.ts.map +1 -0
- package/dist/measure.js +287 -0
- package/dist/measure.js.map +1 -0
- package/dist/plan.d.ts +14 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +247 -0
- package/dist/plan.js.map +1 -0
- package/dist/registry.d.ts +49 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +87 -0
- package/dist/registry.js.map +1 -0
- package/dist/rows.d.ts +39 -0
- package/dist/rows.d.ts.map +1 -0
- package/dist/rows.js +60 -0
- package/dist/rows.js.map +1 -0
- package/dist/scene.d.ts +179 -0
- package/dist/scene.d.ts.map +1 -0
- package/dist/scene.js +265 -0
- package/dist/scene.js.map +1 -0
- package/dist/shelves.d.ts +23 -0
- package/dist/shelves.d.ts.map +1 -0
- package/dist/shelves.js +63 -0
- package/dist/shelves.js.map +1 -0
- package/dist/textures.d.ts +72 -0
- package/dist/textures.d.ts.map +1 -0
- package/dist/textures.js +160 -0
- package/dist/textures.js.map +1 -0
- package/dist/webgl/atlas.d.ts +63 -0
- package/dist/webgl/atlas.d.ts.map +1 -0
- package/dist/webgl/atlas.js +328 -0
- package/dist/webgl/atlas.js.map +1 -0
- package/dist/webgl/backend.d.ts +141 -0
- package/dist/webgl/backend.d.ts.map +1 -0
- package/dist/webgl/backend.js +659 -0
- package/dist/webgl/backend.js.map +1 -0
- package/dist/webgl/composite.d.ts +101 -0
- package/dist/webgl/composite.d.ts.map +1 -0
- package/dist/webgl/composite.js +293 -0
- package/dist/webgl/composite.js.map +1 -0
- package/dist/webgl/draws.d.ts +213 -0
- package/dist/webgl/draws.d.ts.map +1 -0
- package/dist/webgl/draws.js +714 -0
- package/dist/webgl/draws.js.map +1 -0
- package/dist/webgl/filters.d.ts +107 -0
- package/dist/webgl/filters.d.ts.map +1 -0
- package/dist/webgl/filters.js +351 -0
- package/dist/webgl/filters.js.map +1 -0
- package/dist/webgl/gpu-textures.d.ts +44 -0
- package/dist/webgl/gpu-textures.d.ts.map +1 -0
- package/dist/webgl/gpu-textures.js +178 -0
- package/dist/webgl/gpu-textures.js.map +1 -0
- package/dist/webgl/holds.d.ts +97 -0
- package/dist/webgl/holds.d.ts.map +1 -0
- package/dist/webgl/holds.js +453 -0
- package/dist/webgl/holds.js.map +1 -0
- package/dist/webgl/index.d.ts +2 -0
- package/dist/webgl/index.d.ts.map +1 -0
- package/dist/webgl/index.js +4 -0
- package/dist/webgl/index.js.map +1 -0
- package/dist/webgl/kernels.d.ts +64 -0
- package/dist/webgl/kernels.d.ts.map +1 -0
- package/dist/webgl/kernels.js +160 -0
- package/dist/webgl/kernels.js.map +1 -0
- package/dist/webgl/pool.d.ts +71 -0
- package/dist/webgl/pool.d.ts.map +1 -0
- package/dist/webgl/pool.js +192 -0
- package/dist/webgl/pool.js.map +1 -0
- package/dist/webgl/programs.d.ts +130 -0
- package/dist/webgl/programs.d.ts.map +1 -0
- package/dist/webgl/programs.js +582 -0
- package/dist/webgl/programs.js.map +1 -0
- package/dist/webgl/stage.d.ts +67 -0
- package/dist/webgl/stage.d.ts.map +1 -0
- package/dist/webgl/stage.js +173 -0
- package/dist/webgl/stage.js.map +1 -0
- package/dist/webgl/targets.d.ts +172 -0
- package/dist/webgl/targets.d.ts.map +1 -0
- package/dist/webgl/targets.js +323 -0
- package/dist/webgl/targets.js.map +1 -0
- package/package.json +35 -3
- package/src/commands.ts +98 -0
- package/src/filter-plan.ts +755 -0
- package/src/filter-shaders.ts +360 -0
- package/src/filters.ts +220 -0
- package/src/geometry.ts +143 -0
- package/src/index.ts +87 -0
- package/src/keeps.ts +21 -0
- package/src/kernel-shaders.ts +375 -0
- package/src/measure.ts +400 -0
- package/src/plan.ts +331 -0
- package/src/registry.ts +105 -0
- package/src/rows.ts +93 -0
- package/src/scene.ts +297 -0
- package/src/shelves.ts +70 -0
- package/src/textures.ts +198 -0
- package/src/webgl/atlas.ts +370 -0
- package/src/webgl/backend.ts +973 -0
- package/src/webgl/composite.ts +372 -0
- package/src/webgl/draws.ts +902 -0
- package/src/webgl/filters.ts +502 -0
- package/src/webgl/gpu-textures.ts +253 -0
- package/src/webgl/holds.ts +624 -0
- package/src/webgl/index.ts +3 -0
- package/src/webgl/kernels.ts +205 -0
- package/src/webgl/pool.ts +245 -0
- package/src/webgl/programs.ts +620 -0
- package/src/webgl/stage.ts +214 -0
- package/src/webgl/targets.ts +464 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import type { GlState } from "@swf2es/context3d/device";
|
|
2
|
+
import {
|
|
3
|
+
type Commands,
|
|
4
|
+
HELD,
|
|
5
|
+
HOLD_POP,
|
|
6
|
+
HOLD_PUSH,
|
|
7
|
+
RUN,
|
|
8
|
+
STENCIL_POP,
|
|
9
|
+
STENCIL_PUSH,
|
|
10
|
+
} from "../commands.js";
|
|
11
|
+
import { FilterPlan, type KernelObject, planChain } from "../filter-plan.js";
|
|
12
|
+
import type { Filters } from "../filters.js";
|
|
13
|
+
import type { SceneList } from "../scene.js";
|
|
14
|
+
import { Shelves } from "../shelves.js";
|
|
15
|
+
import type { Compositor } from "./composite.js";
|
|
16
|
+
import type { Draws, Masks } from "./draws.js";
|
|
17
|
+
import type { FilterRunner, FilterTarget } from "./filters.js";
|
|
18
|
+
import type { GpuTextures } from "./gpu-textures.js";
|
|
19
|
+
import {
|
|
20
|
+
framebufferOf,
|
|
21
|
+
LAYER_STEP,
|
|
22
|
+
type Layer,
|
|
23
|
+
layerSide,
|
|
24
|
+
type Open,
|
|
25
|
+
objectOf,
|
|
26
|
+
type Target,
|
|
27
|
+
type TargetPool,
|
|
28
|
+
} from "./targets.js";
|
|
29
|
+
|
|
30
|
+
/** The width of the pages held layers and their filters' targets are placed on, side by side. */
|
|
31
|
+
const PAGE_WIDTH = 2048;
|
|
32
|
+
/** Keep content pages below the free pool's budget so they can be reused. */
|
|
33
|
+
const CONTENT_PAGE_BYTES = 64 * 1024 * 1024;
|
|
34
|
+
|
|
35
|
+
/** A rectangle of a page, read and drawn into as a texture of its own (FilterTarget). */
|
|
36
|
+
interface Slot extends FilterTarget {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
page: Layer;
|
|
40
|
+
/** Its page's index in the frame's (HeldLayers.pages). */
|
|
41
|
+
index: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A held layer (HOLD_PUSH): its rectangle on the target, the operations it
|
|
46
|
+
* draws by, its chain of filters, planned, and its targets by the plan's
|
|
47
|
+
* slots; its output once drawn, or null where none is.
|
|
48
|
+
*/
|
|
49
|
+
interface Hold {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
first: number;
|
|
55
|
+
end: number;
|
|
56
|
+
chain: number;
|
|
57
|
+
/** Where its object lies in it, which its ShaderFilters read (filter-plan.ts). */
|
|
58
|
+
object: KernelObject | null;
|
|
59
|
+
plan: FilterPlan;
|
|
60
|
+
targets: (Slot | null)[];
|
|
61
|
+
/** Its slots' records, kept from frame to frame, and the last pass that uses each. */
|
|
62
|
+
slots: Slot[];
|
|
63
|
+
lastUse: number[];
|
|
64
|
+
result: Slot | null;
|
|
65
|
+
/** Its content's rectangle on the pages (Shelves), -1 where it is drawn alone; the wave's. */
|
|
66
|
+
shelf: number;
|
|
67
|
+
wave: number;
|
|
68
|
+
/** Whether its pass of the wave drawing is drawn, or it has none. */
|
|
69
|
+
drawn: boolean;
|
|
70
|
+
/** Its level (HOLD_PUSH), one past the deepest held layer in it. */
|
|
71
|
+
level: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The frame's held layers (HOLD_PUSH), drawn ahead of the stage a level at
|
|
76
|
+
* a time: their content side by side on pages, then their filters' passes
|
|
77
|
+
* in waves, each wave's targets on pages of their own; their outputs left
|
|
78
|
+
* on the pages for the frame's HELD composites.
|
|
79
|
+
*/
|
|
80
|
+
export class HeldLayers {
|
|
81
|
+
private readonly gl: WebGL2RenderingContext;
|
|
82
|
+
private readonly state: GlState;
|
|
83
|
+
private readonly pool: TargetPool;
|
|
84
|
+
private readonly draws: Draws;
|
|
85
|
+
private readonly compositor: Compositor;
|
|
86
|
+
private readonly gpu: GpuTextures;
|
|
87
|
+
/** The filters' passes, the backend's. */
|
|
88
|
+
private filters!: FilterRunner<Layer>;
|
|
89
|
+
/**
|
|
90
|
+
* The frame's held layers (HOLD_PUSH); the pages they took, null once
|
|
91
|
+
* given back; the last wave each is read in, and whether an output is on
|
|
92
|
+
* it, which keeps it for the frame.
|
|
93
|
+
*/
|
|
94
|
+
private readonly holds: Hold[] = [];
|
|
95
|
+
private readonly pages: (Layer | null)[] = [];
|
|
96
|
+
private readonly pageUntil: number[] = [];
|
|
97
|
+
private readonly pageKept: boolean[] = [];
|
|
98
|
+
/** A page drawn into whole, a held layer's rectangle of one, and a held output composited. */
|
|
99
|
+
private readonly pageOpen: Open = { layer: null, msaa: null, x: 0, y: 0, width: 0, height: 0 };
|
|
100
|
+
private readonly holdOpen: Open = { layer: null, msaa: null, x: 0, y: 0, width: 0, height: 0 };
|
|
101
|
+
private readonly heldOpen: Open = { layer: null, msaa: null, x: 0, y: 0, width: 0, height: 0 };
|
|
102
|
+
private readonly shelves = new Shelves();
|
|
103
|
+
/** The masks pushed on the held layer whose content is drawn (drawHoldContent). */
|
|
104
|
+
private readonly masks: Masks = { depth: 0, past: 0 };
|
|
105
|
+
/** The largest side a texture may have. */
|
|
106
|
+
private textureMost = 4096;
|
|
107
|
+
|
|
108
|
+
constructor(
|
|
109
|
+
gl: WebGL2RenderingContext,
|
|
110
|
+
state: GlState,
|
|
111
|
+
pool: TargetPool,
|
|
112
|
+
draws: Draws,
|
|
113
|
+
compositor: Compositor,
|
|
114
|
+
gpu: GpuTextures,
|
|
115
|
+
) {
|
|
116
|
+
this.gl = gl;
|
|
117
|
+
this.state = state;
|
|
118
|
+
this.pool = pool;
|
|
119
|
+
this.draws = draws;
|
|
120
|
+
this.compositor = compositor;
|
|
121
|
+
this.gpu = gpu;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The filters' passes and the largest side a texture may have, as the
|
|
126
|
+
* context comes or comes back.
|
|
127
|
+
*/
|
|
128
|
+
setUp(filters: FilterRunner<Layer>, textureMost: number): void {
|
|
129
|
+
this.filters = filters;
|
|
130
|
+
this.textureMost = textureMost;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The frame's held layers (HOLD_PUSH), drawn ahead of the stage side by
|
|
135
|
+
* side: their content on pages of their own, one bind, clear and resolve
|
|
136
|
+
* a page; then their filters' passes in waves, the k-th pass of every
|
|
137
|
+
* chain at once, each wave's targets on pages of its own, which no pass
|
|
138
|
+
* of it reads. Each layer's output is left on its page for HELD. A layer
|
|
139
|
+
* larger than a page is drawn alone, as a layer of its own.
|
|
140
|
+
*/
|
|
141
|
+
drawHolds(commands: Commands, scene: SceneList, filters: Filters, target: Target): void {
|
|
142
|
+
const count = this.gatherHolds(commands);
|
|
143
|
+
// A level at a time, which the planner gives in order: a held layer's content composites
|
|
144
|
+
// those in it, of the levels before.
|
|
145
|
+
for (let from = 0; from < count; ) {
|
|
146
|
+
let to = from + 1;
|
|
147
|
+
while (to < count && this.holds[to].level === this.holds[from].level) {
|
|
148
|
+
to++;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.drawHoldLevel(from, to, commands, scene, filters, target);
|
|
152
|
+
from = to;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Held layers `from` up to `to`, of one level, drawn side by side (drawHolds). */
|
|
157
|
+
private drawHoldLevel(
|
|
158
|
+
from: number,
|
|
159
|
+
to: number,
|
|
160
|
+
commands: Commands,
|
|
161
|
+
scene: SceneList,
|
|
162
|
+
filters: Filters,
|
|
163
|
+
target: Target,
|
|
164
|
+
): void {
|
|
165
|
+
const gl = this.gl;
|
|
166
|
+
const state = this.state;
|
|
167
|
+
const holds = this.holds;
|
|
168
|
+
// The pages this level takes, from here on, which its waves give back.
|
|
169
|
+
const levelPages = this.pages.length;
|
|
170
|
+
const shelves = this.shelves;
|
|
171
|
+
this.draws.forgetRow();
|
|
172
|
+
// Their content, each at the size its own layer would take, on pages no taller than the
|
|
173
|
+
// frame's samples fit (fittingSamples), so that each takes them as its own layer did.
|
|
174
|
+
let area = 0;
|
|
175
|
+
let widest = 0;
|
|
176
|
+
for (let h = from; h < to; h++) {
|
|
177
|
+
area += layerSide(holds[h].width) * layerSide(holds[h].height);
|
|
178
|
+
widest = Math.max(widest, layerSide(holds[h].width));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const samples = this.pool.frameSamples;
|
|
182
|
+
let width = this.pageWidth(area, widest);
|
|
183
|
+
const fitting = Math.max(
|
|
184
|
+
LAYER_STEP,
|
|
185
|
+
Math.floor(CONTENT_PAGE_BYTES / (width * samples * 8) / LAYER_STEP) * LAYER_STEP,
|
|
186
|
+
);
|
|
187
|
+
shelves.reset(width, samples > 1 ? Math.min(this.textureMost, fitting) : this.textureMost);
|
|
188
|
+
for (let h = from; h < to; h++) {
|
|
189
|
+
const hold = holds[h];
|
|
190
|
+
hold.shelf = shelves.add(layerSide(hold.width), layerSide(hold.height));
|
|
191
|
+
if (hold.shelf < 0) {
|
|
192
|
+
this.drawHoldAlone(hold, commands, scene, filters, target);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (let p = 0; p < shelves.pages; p++) {
|
|
197
|
+
const index = this.takePage(width, shelves.heights[p]);
|
|
198
|
+
const page = this.pages[index] as Layer;
|
|
199
|
+
const whole = this.pageOpen;
|
|
200
|
+
whole.layer = page;
|
|
201
|
+
whole.msaa = this.pool.layerMsaa(page, samples);
|
|
202
|
+
whole.width = page.width;
|
|
203
|
+
whole.height = page.height;
|
|
204
|
+
state.bindFramebuffer(framebufferOf(whole));
|
|
205
|
+
state.scissor(null);
|
|
206
|
+
state.stencil(null);
|
|
207
|
+
state.colorMask(15);
|
|
208
|
+
gl.clearColor(0, 0, 0, 0);
|
|
209
|
+
gl.clearStencil(0);
|
|
210
|
+
gl.stencilMask(0xff);
|
|
211
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
212
|
+
for (let h = from; h < to; h++) {
|
|
213
|
+
const hold = holds[h];
|
|
214
|
+
if (hold.shelf < 0 || shelves.page[hold.shelf] !== p) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const content = this.holdOpen;
|
|
219
|
+
content.layer = page;
|
|
220
|
+
content.msaa = whole.msaa;
|
|
221
|
+
content.x = hold.x;
|
|
222
|
+
content.y = hold.y;
|
|
223
|
+
content.width = hold.width;
|
|
224
|
+
content.height = hold.height;
|
|
225
|
+
content.ox = shelves.x[hold.shelf];
|
|
226
|
+
content.oy = shelves.y[hold.shelf];
|
|
227
|
+
// Kept to its rectangle by the viewport alone, as the composites in it scissor themselves.
|
|
228
|
+
this.draws.drawInto(content, target);
|
|
229
|
+
this.drawHoldContent(commands, hold, scene, content);
|
|
230
|
+
this.slotOf(hold, 0, index, content.ox, content.oy, hold.width, hold.height);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.pool.resolve(whole, page.width, page.height);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Their filters, planned, then drawn a wave at a time: each wave's new targets on pages of its
|
|
237
|
+
// own, which none of its passes reads, as each reads what earlier waves drew. A page goes back
|
|
238
|
+
// once no pass after reads it, unless an output is on it.
|
|
239
|
+
let waves = 0;
|
|
240
|
+
for (let h = from; h < to; h++) {
|
|
241
|
+
const hold = holds[h];
|
|
242
|
+
if (hold.shelf < 0) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const plan = hold.plan;
|
|
247
|
+
planChain(
|
|
248
|
+
plan,
|
|
249
|
+
filters,
|
|
250
|
+
hold.chain,
|
|
251
|
+
hold.width,
|
|
252
|
+
hold.height,
|
|
253
|
+
target.resolution,
|
|
254
|
+
this.gpu.hasTexture,
|
|
255
|
+
this.filters.links,
|
|
256
|
+
hold.object,
|
|
257
|
+
);
|
|
258
|
+
hold.targets.length = plan.slots;
|
|
259
|
+
hold.targets.fill(null, 1);
|
|
260
|
+
hold.lastUse.length = plan.slots;
|
|
261
|
+
hold.lastUse.fill(-1);
|
|
262
|
+
for (let k = 0; k < plan.count; k++) {
|
|
263
|
+
const pass = plan.passes[k];
|
|
264
|
+
hold.lastUse[pass.input] = k;
|
|
265
|
+
hold.lastUse[pass.output] = k;
|
|
266
|
+
if (pass.blurred >= 0) {
|
|
267
|
+
hold.lastUse[pass.blurred] = k;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this.notePage(hold, 0);
|
|
272
|
+
waves = Math.max(waves, plan.count);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (let k = 0; k < waves; k++) {
|
|
276
|
+
area = 0;
|
|
277
|
+
widest = 0;
|
|
278
|
+
for (let h = from; h < to; h++) {
|
|
279
|
+
const hold = holds[h];
|
|
280
|
+
const plan = hold.plan;
|
|
281
|
+
if (hold.shelf >= 0 && k < plan.count && !hold.targets[plan.passes[k].output]) {
|
|
282
|
+
const slot = plan.passes[k].output;
|
|
283
|
+
area += layerSide(plan.slotWidth[slot]) * layerSide(plan.slotHeight[slot]);
|
|
284
|
+
widest = Math.max(widest, layerSide(plan.slotWidth[slot]));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
width = this.pageWidth(area, widest);
|
|
289
|
+
shelves.reset(width, this.textureMost);
|
|
290
|
+
for (let h = from; h < to; h++) {
|
|
291
|
+
const hold = holds[h];
|
|
292
|
+
const plan = hold.plan;
|
|
293
|
+
hold.wave = -1;
|
|
294
|
+
if (hold.shelf >= 0 && k < plan.count && !hold.targets[plan.passes[k].output]) {
|
|
295
|
+
const slot = plan.passes[k].output;
|
|
296
|
+
hold.wave = shelves.add(
|
|
297
|
+
layerSide(plan.slotWidth[slot]),
|
|
298
|
+
layerSide(plan.slotHeight[slot]),
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const first = this.pages.length;
|
|
304
|
+
for (let p = 0; p < shelves.pages; p++) {
|
|
305
|
+
const page = this.pages[this.takePage(width, shelves.heights[p])] as Layer;
|
|
306
|
+
state.bindFramebuffer(page.framebuffer);
|
|
307
|
+
state.scissor(null);
|
|
308
|
+
state.colorMask(15);
|
|
309
|
+
gl.clearColor(0, 0, 0, 0);
|
|
310
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
for (let h = from; h < to; h++) {
|
|
314
|
+
const hold = holds[h];
|
|
315
|
+
if (hold.wave >= 0) {
|
|
316
|
+
const plan = hold.plan;
|
|
317
|
+
const slot = plan.passes[k].output;
|
|
318
|
+
this.slotOf(
|
|
319
|
+
hold,
|
|
320
|
+
slot,
|
|
321
|
+
first + shelves.page[hold.wave],
|
|
322
|
+
shelves.x[hold.wave],
|
|
323
|
+
shelves.y[hold.wave],
|
|
324
|
+
plan.slotWidth[slot],
|
|
325
|
+
plan.slotHeight[slot],
|
|
326
|
+
);
|
|
327
|
+
this.notePage(hold, slot);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// The passes drawing into one page together, so that it is bound once: a target drawn into
|
|
332
|
+
// again, as a blur's runs take turns between two, is on an earlier wave's page.
|
|
333
|
+
let left = 0;
|
|
334
|
+
for (let h = from; h < to; h++) {
|
|
335
|
+
const hold = holds[h];
|
|
336
|
+
hold.drawn = !(hold.shelf >= 0 && k < hold.plan.count);
|
|
337
|
+
left += hold.drawn ? 0 : 1;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
while (left > 0) {
|
|
341
|
+
let page = -1;
|
|
342
|
+
for (let h = from; h < to; h++) {
|
|
343
|
+
const hold = holds[h];
|
|
344
|
+
if (hold.drawn) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const output = hold.targets[hold.plan.passes[k].output] as Slot;
|
|
349
|
+
if (page < 0) {
|
|
350
|
+
page = output.index;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (output.index === page) {
|
|
354
|
+
this.filters.drawPass(hold.plan, k, hold.targets, false);
|
|
355
|
+
hold.drawn = true;
|
|
356
|
+
left--;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
for (let index = levelPages; index < this.pages.length; index++) {
|
|
362
|
+
const page = this.pages[index];
|
|
363
|
+
if (page && !this.pageKept[index] && this.pageUntil[index] <= k) {
|
|
364
|
+
this.pool.giveLayer(page);
|
|
365
|
+
this.pages[index] = null;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
for (let h = from; h < to; h++) {
|
|
371
|
+
const hold = holds[h];
|
|
372
|
+
if (hold.shelf >= 0) {
|
|
373
|
+
hold.result = hold.targets[hold.plan.result];
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* The width of a page for rectangles of `area` in all, the widest `widest`:
|
|
380
|
+
* about square, in LAYER_STEP's steps, at most PAGE_WIDTH.
|
|
381
|
+
*/
|
|
382
|
+
private pageWidth(area: number, widest: number): number {
|
|
383
|
+
const square = Math.ceil(Math.sqrt(area) / LAYER_STEP) * LAYER_STEP;
|
|
384
|
+
return Math.min(this.textureMost, PAGE_WIDTH, Math.max(widest, square));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** A page of at least `width` by `height` taken for the frame: its index in `pages`. */
|
|
388
|
+
private takePage(width: number, height: number): number {
|
|
389
|
+
return this.addPage(this.pool.takeLayer(width, height), false);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* `layer` added to the frame's pages, read by no pass yet, kept for the
|
|
394
|
+
* frame where `kept`: its index in `pages`.
|
|
395
|
+
*/
|
|
396
|
+
private addPage(layer: Layer, kept: boolean): number {
|
|
397
|
+
const index = this.pages.length;
|
|
398
|
+
this.pages.push(layer);
|
|
399
|
+
this.pageUntil.push(-1);
|
|
400
|
+
this.pageKept.push(kept);
|
|
401
|
+
return index;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** Slot `slot` of `hold` noted on its page: read until its last pass, or kept as its output. */
|
|
405
|
+
private notePage(hold: Hold, slot: number): void {
|
|
406
|
+
const index = (hold.targets[slot] as Slot).index;
|
|
407
|
+
this.pageUntil[index] = Math.max(this.pageUntil[index], hold.lastUse[slot]);
|
|
408
|
+
if (slot === hold.plan.result) {
|
|
409
|
+
this.pageKept[index] = true;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** The held layers' rectangles and operations (HOLD_PUSH), which the commands begin with: how many. */
|
|
414
|
+
private gatherHolds(commands: Commands): number {
|
|
415
|
+
const ops = commands.ops;
|
|
416
|
+
const params = commands.params;
|
|
417
|
+
const holds = this.holds;
|
|
418
|
+
let count = 0;
|
|
419
|
+
for (let k = 0; k < commands.length && ops[k * 4] === HOLD_PUSH; k++) {
|
|
420
|
+
const from = ops[k * 4 + 1];
|
|
421
|
+
let hold = holds[count];
|
|
422
|
+
if (!hold) {
|
|
423
|
+
hold = {
|
|
424
|
+
x: 0,
|
|
425
|
+
y: 0,
|
|
426
|
+
width: 0,
|
|
427
|
+
height: 0,
|
|
428
|
+
first: 0,
|
|
429
|
+
end: 0,
|
|
430
|
+
chain: 0,
|
|
431
|
+
object: null,
|
|
432
|
+
plan: new FilterPlan(),
|
|
433
|
+
targets: [],
|
|
434
|
+
slots: [],
|
|
435
|
+
lastUse: [],
|
|
436
|
+
result: null,
|
|
437
|
+
shelf: -1,
|
|
438
|
+
wave: -1,
|
|
439
|
+
drawn: false,
|
|
440
|
+
level: 0,
|
|
441
|
+
};
|
|
442
|
+
holds[count] = hold;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
count++;
|
|
446
|
+
hold.level = ops[k * 4 + 3];
|
|
447
|
+
hold.x = params[from];
|
|
448
|
+
hold.y = params[from + 1];
|
|
449
|
+
hold.width = params[from + 2];
|
|
450
|
+
hold.height = params[from + 3];
|
|
451
|
+
hold.object = objectOf(params, from + 4);
|
|
452
|
+
hold.first = k + 1;
|
|
453
|
+
|
|
454
|
+
while (ops[k * 4] !== HOLD_POP) {
|
|
455
|
+
k++;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
hold.end = k;
|
|
459
|
+
hold.chain = ops[k * 4 + 1];
|
|
460
|
+
hold.result = null;
|
|
461
|
+
hold.targets.length = 0;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return count;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Slot `slot` of `hold`'s targets: the rectangle of page `index` from (x,
|
|
469
|
+
* y) a target of `width` by `height` takes, as its own layer would; its
|
|
470
|
+
* record kept from frame to frame.
|
|
471
|
+
*/
|
|
472
|
+
private slotOf(
|
|
473
|
+
hold: Hold,
|
|
474
|
+
slot: number,
|
|
475
|
+
index: number,
|
|
476
|
+
x: number,
|
|
477
|
+
y: number,
|
|
478
|
+
width: number,
|
|
479
|
+
height: number,
|
|
480
|
+
): Slot {
|
|
481
|
+
const page = this.pages[index] as Layer;
|
|
482
|
+
let record = hold.slots[slot];
|
|
483
|
+
if (!record) {
|
|
484
|
+
record = {
|
|
485
|
+
framebuffer: page.framebuffer,
|
|
486
|
+
texture: page.texture,
|
|
487
|
+
x,
|
|
488
|
+
y,
|
|
489
|
+
width,
|
|
490
|
+
height,
|
|
491
|
+
page,
|
|
492
|
+
index,
|
|
493
|
+
};
|
|
494
|
+
hold.slots[slot] = record;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
record.framebuffer = page.framebuffer;
|
|
498
|
+
record.texture = page.texture;
|
|
499
|
+
record.x = x;
|
|
500
|
+
record.y = y;
|
|
501
|
+
record.width = layerSide(width);
|
|
502
|
+
record.height = layerSide(height);
|
|
503
|
+
record.page = page;
|
|
504
|
+
record.index = index;
|
|
505
|
+
hold.targets[slot] = record;
|
|
506
|
+
return record;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Held layer `hold`'s draws, masks and held layers' composites into
|
|
511
|
+
* `open`, bound, its stencil starting clear.
|
|
512
|
+
*/
|
|
513
|
+
private drawHoldContent(commands: Commands, hold: Hold, scene: SceneList, open: Open): void {
|
|
514
|
+
const state = this.state;
|
|
515
|
+
const ops = commands.ops;
|
|
516
|
+
const masks = this.masks;
|
|
517
|
+
masks.depth = 0;
|
|
518
|
+
masks.past = 0;
|
|
519
|
+
state.stencil(null);
|
|
520
|
+
state.colorMask(15);
|
|
521
|
+
for (let k = hold.first; k < hold.end; k++) {
|
|
522
|
+
const at = k * 4;
|
|
523
|
+
switch (ops[at]) {
|
|
524
|
+
case RUN:
|
|
525
|
+
this.draws.drawRun(scene, ops[at + 1], ops[at + 1] + ops[at + 2], k);
|
|
526
|
+
break;
|
|
527
|
+
case STENCIL_PUSH:
|
|
528
|
+
case STENCIL_POP:
|
|
529
|
+
this.draws.drawMask(
|
|
530
|
+
scene,
|
|
531
|
+
ops[at + 1],
|
|
532
|
+
ops[at + 1] + ops[at + 2],
|
|
533
|
+
ops[at] === STENCIL_PUSH,
|
|
534
|
+
masks,
|
|
535
|
+
k,
|
|
536
|
+
);
|
|
537
|
+
break;
|
|
538
|
+
case HELD:
|
|
539
|
+
this.draws.maskBy(masks);
|
|
540
|
+
this.compositeHeld(ops[at + 1], open, ops[at + 2], ops[at + 3]);
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
state.stencil(null);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/** Held output `held` composited onto `under`, bound, in blend mode `mode`, coloured by `row`. */
|
|
549
|
+
compositeHeld(held: number, under: Open, mode: number, row: number): void {
|
|
550
|
+
const hold = this.holds[held];
|
|
551
|
+
const result = hold.result;
|
|
552
|
+
if (!result) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const open = this.heldOpen;
|
|
557
|
+
open.layer = result.page;
|
|
558
|
+
open.x = hold.x;
|
|
559
|
+
open.y = hold.y;
|
|
560
|
+
open.width = hold.width;
|
|
561
|
+
open.height = hold.height;
|
|
562
|
+
open.ox = result.x;
|
|
563
|
+
open.oy = result.y;
|
|
564
|
+
this.compositor.compositeLayer(open, under, mode, row);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Held layer `hold`, too large for a page, drawn as a layer of its own and
|
|
569
|
+
* filtered there, its output left in a page of its own.
|
|
570
|
+
*/
|
|
571
|
+
private drawHoldAlone(
|
|
572
|
+
hold: Hold,
|
|
573
|
+
commands: Commands,
|
|
574
|
+
scene: SceneList,
|
|
575
|
+
filters: Filters,
|
|
576
|
+
target: Target,
|
|
577
|
+
): void {
|
|
578
|
+
const gl = this.gl;
|
|
579
|
+
const state = this.state;
|
|
580
|
+
const layer = this.pool.takeLayer(hold.width, hold.height, true);
|
|
581
|
+
const open: Open = {
|
|
582
|
+
layer,
|
|
583
|
+
msaa: this.pool.layerMsaa(layer, this.pool.frameSamples),
|
|
584
|
+
x: hold.x,
|
|
585
|
+
y: hold.y,
|
|
586
|
+
width: hold.width,
|
|
587
|
+
height: hold.height,
|
|
588
|
+
};
|
|
589
|
+
this.draws.drawInto(open, target);
|
|
590
|
+
state.scissor(null);
|
|
591
|
+
state.colorMask(15);
|
|
592
|
+
state.stencil(null);
|
|
593
|
+
gl.clearColor(0, 0, 0, 0);
|
|
594
|
+
gl.clearStencil(0);
|
|
595
|
+
gl.stencilMask(0xff);
|
|
596
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
597
|
+
this.drawHoldContent(commands, hold, scene, open);
|
|
598
|
+
this.pool.resolve(open, layer.width, layer.height);
|
|
599
|
+
const output = this.filters.run(
|
|
600
|
+
filters,
|
|
601
|
+
hold.chain,
|
|
602
|
+
layer,
|
|
603
|
+
hold.width,
|
|
604
|
+
hold.height,
|
|
605
|
+
target.resolution,
|
|
606
|
+
hold.object,
|
|
607
|
+
);
|
|
608
|
+
const index = this.addPage(output, true);
|
|
609
|
+
hold.result = this.slotOf(hold, 0, index, 0, 0, output.width, output.height);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** The pages the frame took given back, as it ends. */
|
|
613
|
+
givePages(): void {
|
|
614
|
+
for (const page of this.pages) {
|
|
615
|
+
if (page) {
|
|
616
|
+
this.pool.giveLayer(page);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
this.pages.length = 0;
|
|
621
|
+
this.pageUntil.length = 0;
|
|
622
|
+
this.pageKept.length = 0;
|
|
623
|
+
}
|
|
624
|
+
}
|