@multiplekex/shallot 0.1.9 → 0.1.10
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/package.json +1 -1
- package/src/core/state.ts +2 -0
- package/src/extras/arrows/index.ts +13 -8
- package/src/extras/gradient/index.ts +1050 -0
- package/src/extras/index.ts +1 -0
- package/src/extras/lines/index.ts +13 -8
- package/src/extras/text/index.ts +11 -7
- package/src/standard/compute/graph.ts +10 -12
- package/src/standard/compute/index.ts +1 -0
- package/src/standard/compute/inspect.ts +34 -52
- package/src/standard/compute/pass.ts +23 -0
- package/src/standard/render/camera.ts +7 -2
- package/src/standard/render/forward.ts +99 -89
- package/src/standard/render/index.ts +68 -44
- package/src/standard/render/mesh/index.ts +190 -19
- package/src/standard/render/pass.ts +63 -0
- package/src/standard/render/postprocess.ts +241 -57
- package/src/standard/render/scene.ts +87 -2
- package/src/standard/render/surface/compile.ts +74 -0
- package/src/standard/render/surface/index.ts +116 -0
- package/src/standard/render/surface/structs.ts +50 -0
- package/src/standard/render/transparent.ts +62 -65
- package/src/standard/render/material/index.ts +0 -92
- package/src/standard/render/opaque.ts +0 -44
package/src/extras/index.ts
CHANGED
|
@@ -5,9 +5,10 @@ import {
|
|
|
5
5
|
Render,
|
|
6
6
|
RenderPlugin,
|
|
7
7
|
DEPTH_FORMAT,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type
|
|
8
|
+
Pass,
|
|
9
|
+
registerDraw,
|
|
10
|
+
type Draw,
|
|
11
|
+
type SharedPassContext,
|
|
11
12
|
} from "../../standard/render";
|
|
12
13
|
import { Transform } from "../../standard/transforms";
|
|
13
14
|
|
|
@@ -316,15 +317,18 @@ export function createLinesPipeline(
|
|
|
316
317
|
});
|
|
317
318
|
}
|
|
318
319
|
|
|
319
|
-
function
|
|
320
|
+
function createLinesDraw(config: LinesConfig): Draw {
|
|
320
321
|
let pipeline: GPURenderPipeline | null = null;
|
|
321
322
|
let bindGroup: GPUBindGroup | null = null;
|
|
322
323
|
|
|
323
324
|
return {
|
|
324
325
|
id: "lines",
|
|
326
|
+
pass: Pass.Transparent,
|
|
325
327
|
order: 0,
|
|
326
328
|
|
|
327
|
-
|
|
329
|
+
execute() {},
|
|
330
|
+
|
|
331
|
+
draw(pass: GPURenderPassEncoder, ctx: SharedPassContext) {
|
|
328
332
|
const count = config.getCount();
|
|
329
333
|
if (count === 0) return;
|
|
330
334
|
|
|
@@ -377,7 +381,8 @@ const LinesSystem: System = {
|
|
|
377
381
|
entityIdArray[count++] = eid;
|
|
378
382
|
}
|
|
379
383
|
|
|
380
|
-
|
|
384
|
+
const uploadCount = state.maxEid + 1;
|
|
385
|
+
device.queue.writeBuffer(lines.buffer, 0, LineData.data, 0, uploadCount * 12);
|
|
381
386
|
device.queue.writeBuffer(lines.entityIds, 0, entityIdArray, 0, count);
|
|
382
387
|
lines.count = count;
|
|
383
388
|
},
|
|
@@ -407,9 +412,9 @@ export const LinesPlugin: Plugin = {
|
|
|
407
412
|
|
|
408
413
|
state.setResource(Lines, linesState);
|
|
409
414
|
|
|
410
|
-
|
|
415
|
+
registerDraw(
|
|
411
416
|
state,
|
|
412
|
-
|
|
417
|
+
createLinesDraw({
|
|
413
418
|
scene: render.scene,
|
|
414
419
|
lines: linesState.buffer,
|
|
415
420
|
entityIds: linesState.entityIds,
|
package/src/extras/text/index.ts
CHANGED
|
@@ -14,9 +14,10 @@ import {
|
|
|
14
14
|
Render,
|
|
15
15
|
RenderPlugin,
|
|
16
16
|
DEPTH_FORMAT,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
type
|
|
17
|
+
Pass,
|
|
18
|
+
registerDraw,
|
|
19
|
+
type Draw,
|
|
20
|
+
type SharedPassContext,
|
|
20
21
|
} from "../../standard/render";
|
|
21
22
|
import { Transform } from "../../standard/transforms";
|
|
22
23
|
|
|
@@ -646,15 +647,18 @@ export interface TextConfig {
|
|
|
646
647
|
getCount: () => number;
|
|
647
648
|
}
|
|
648
649
|
|
|
649
|
-
function
|
|
650
|
+
function createTextDraw(config: TextConfig): Draw {
|
|
650
651
|
let pipeline: GPURenderPipeline | null = null;
|
|
651
652
|
let bindGroup: GPUBindGroup | null = null;
|
|
652
653
|
|
|
653
654
|
return {
|
|
654
655
|
id: "text",
|
|
656
|
+
pass: Pass.Transparent,
|
|
655
657
|
order: 2,
|
|
656
658
|
|
|
657
|
-
|
|
659
|
+
execute() {},
|
|
660
|
+
|
|
661
|
+
draw(pass: GPURenderPassEncoder, ctx: SharedPassContext) {
|
|
658
662
|
const count = config.getCount();
|
|
659
663
|
if (count === 0) return;
|
|
660
664
|
|
|
@@ -805,9 +809,9 @@ export const TextPlugin: Plugin = {
|
|
|
805
809
|
|
|
806
810
|
state.setResource(TextResource, textState);
|
|
807
811
|
|
|
808
|
-
|
|
812
|
+
registerDraw(
|
|
809
813
|
state,
|
|
810
|
-
|
|
814
|
+
createTextDraw({
|
|
811
815
|
scene: render.scene,
|
|
812
816
|
glyphs: textState.buffer,
|
|
813
817
|
atlas: atlas.textureView,
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { CycleError } from "../../core";
|
|
2
2
|
import { inspect as inspectGraph, type GraphInspection } from "./inspect";
|
|
3
|
+
import { Pass, PASS_ORDER } from "./pass";
|
|
3
4
|
|
|
4
5
|
export type ResourceId = string;
|
|
5
6
|
export type NodeId = string;
|
|
6
|
-
export type Phase = "opaque" | "transparent" | "postprocess";
|
|
7
|
-
|
|
8
|
-
const PHASE_ORDER: Phase[] = ["opaque", "transparent", "postprocess"];
|
|
9
7
|
|
|
10
8
|
export interface ResourceRef {
|
|
11
9
|
id: ResourceId;
|
|
@@ -29,7 +27,7 @@ export interface ExecutionContext {
|
|
|
29
27
|
|
|
30
28
|
export interface ComputeNode {
|
|
31
29
|
readonly id: NodeId;
|
|
32
|
-
readonly
|
|
30
|
+
readonly pass?: Pass;
|
|
33
31
|
readonly inputs: readonly ResourceRef[];
|
|
34
32
|
readonly outputs: readonly ResourceRef[];
|
|
35
33
|
readonly execute: (ctx: ExecutionContext) => void;
|
|
@@ -113,20 +111,20 @@ function compile(nodes: ComputeNode[]): ExecutionPlan {
|
|
|
113
111
|
return { sorted: [] };
|
|
114
112
|
}
|
|
115
113
|
|
|
116
|
-
const
|
|
117
|
-
for (const
|
|
118
|
-
|
|
114
|
+
const byPass = new Map<Pass, ComputeNode[]>();
|
|
115
|
+
for (const pass of PASS_ORDER) {
|
|
116
|
+
byPass.set(pass, []);
|
|
119
117
|
}
|
|
120
118
|
|
|
121
119
|
for (const node of nodes) {
|
|
122
|
-
const
|
|
123
|
-
|
|
120
|
+
const pass = node.pass ?? Pass.Opaque;
|
|
121
|
+
byPass.get(pass)!.push(node);
|
|
124
122
|
}
|
|
125
123
|
|
|
126
124
|
const sorted: ComputeNode[] = [];
|
|
127
|
-
for (const
|
|
128
|
-
const
|
|
129
|
-
sorted.push(...topoSort(
|
|
125
|
+
for (const pass of PASS_ORDER) {
|
|
126
|
+
const passNodes = byPass.get(pass)!;
|
|
127
|
+
sorted.push(...topoSort(passNodes));
|
|
130
128
|
}
|
|
131
129
|
|
|
132
130
|
return { sorted };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ComputeNode, NodeId,
|
|
1
|
+
import type { ComputeNode, NodeId, ResourceId, ResourceRef } from "./graph";
|
|
2
2
|
import type { FrameTiming } from "./timing";
|
|
3
|
+
import { Pass, PASS_ORDER } from "./pass";
|
|
3
4
|
|
|
4
5
|
export interface NodeInfo {
|
|
5
6
|
readonly id: NodeId;
|
|
6
|
-
readonly
|
|
7
|
+
readonly pass: Pass;
|
|
7
8
|
readonly inputs: readonly ResourceRef[];
|
|
8
9
|
readonly outputs: readonly ResourceRef[];
|
|
9
10
|
}
|
|
@@ -18,15 +19,13 @@ export interface GraphInspection {
|
|
|
18
19
|
readonly nodes: readonly NodeInfo[];
|
|
19
20
|
readonly edges: readonly EdgeInfo[];
|
|
20
21
|
readonly executionOrder: readonly NodeId[];
|
|
21
|
-
readonly
|
|
22
|
+
readonly byPass: ReadonlyMap<Pass, readonly NodeId[]>;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
const PHASE_ORDER: Phase[] = ["opaque", "transparent", "postprocess"];
|
|
25
|
-
|
|
26
25
|
export function inspect(nodes: readonly ComputeNode[]): GraphInspection {
|
|
27
26
|
const nodeInfos: NodeInfo[] = nodes.map((n) => ({
|
|
28
27
|
id: n.id,
|
|
29
|
-
|
|
28
|
+
pass: n.pass ?? Pass.Opaque,
|
|
30
29
|
inputs: n.inputs,
|
|
31
30
|
outputs: n.outputs,
|
|
32
31
|
}));
|
|
@@ -52,17 +51,17 @@ export function inspect(nodes: readonly ComputeNode[]): GraphInspection {
|
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
const
|
|
56
|
-
for (const
|
|
57
|
-
|
|
54
|
+
const byPass = new Map<Pass, NodeId[]>();
|
|
55
|
+
for (const pass of PASS_ORDER) {
|
|
56
|
+
byPass.set(pass, []);
|
|
58
57
|
}
|
|
59
58
|
for (const n of nodeInfos) {
|
|
60
|
-
|
|
59
|
+
byPass.get(n.pass)!.push(n.id);
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
const executionOrder = topoSortIds(nodes);
|
|
64
63
|
|
|
65
|
-
return { nodes: nodeInfos, edges, executionOrder,
|
|
64
|
+
return { nodes: nodeInfos, edges, executionOrder, byPass };
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
function topoSortIds(nodes: readonly ComputeNode[]): NodeId[] {
|
|
@@ -75,52 +74,34 @@ function topoSortIds(nodes: readonly ComputeNode[]): NodeId[] {
|
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
for (const node of nodes) {
|
|
82
|
-
adjacency.set(node, []);
|
|
83
|
-
inDegree.set(node, 0);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const node of nodes) {
|
|
87
|
-
for (const input of node.inputs) {
|
|
88
|
-
const producer = producers.get(input.id);
|
|
89
|
-
if (producer) {
|
|
90
|
-
adjacency.get(producer)!.push(node);
|
|
91
|
-
inDegree.set(node, inDegree.get(node)! + 1);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const byPhase = new Map<Phase, ComputeNode[]>();
|
|
97
|
-
for (const phase of PHASE_ORDER) {
|
|
98
|
-
byPhase.set(phase, []);
|
|
77
|
+
const byPass = new Map<Pass, ComputeNode[]>();
|
|
78
|
+
for (const pass of PASS_ORDER) {
|
|
79
|
+
byPass.set(pass, []);
|
|
99
80
|
}
|
|
100
81
|
for (const node of nodes) {
|
|
101
|
-
const
|
|
102
|
-
|
|
82
|
+
const pass = node.pass ?? Pass.Opaque;
|
|
83
|
+
byPass.get(pass)!.push(node);
|
|
103
84
|
}
|
|
104
85
|
|
|
105
86
|
const sorted: NodeId[] = [];
|
|
106
|
-
for (const
|
|
107
|
-
const
|
|
108
|
-
const
|
|
87
|
+
for (const pass of PASS_ORDER) {
|
|
88
|
+
const passNodes = byPass.get(pass)!;
|
|
89
|
+
const passInDegree = new Map<ComputeNode, number>();
|
|
109
90
|
|
|
110
|
-
for (const node of
|
|
91
|
+
for (const node of passNodes) {
|
|
111
92
|
let degree = 0;
|
|
112
93
|
for (const input of node.inputs) {
|
|
113
94
|
const producer = producers.get(input.id);
|
|
114
|
-
if (producer &&
|
|
95
|
+
if (producer && passNodes.includes(producer)) {
|
|
115
96
|
degree++;
|
|
116
97
|
}
|
|
117
98
|
}
|
|
118
|
-
|
|
99
|
+
passInDegree.set(node, degree);
|
|
119
100
|
}
|
|
120
101
|
|
|
121
102
|
const queue: ComputeNode[] = [];
|
|
122
|
-
for (const node of
|
|
123
|
-
if (
|
|
103
|
+
for (const node of passNodes) {
|
|
104
|
+
if (passInDegree.get(node) === 0) {
|
|
124
105
|
queue.push(node);
|
|
125
106
|
}
|
|
126
107
|
}
|
|
@@ -130,12 +111,12 @@ function topoSortIds(nodes: readonly ComputeNode[]): NodeId[] {
|
|
|
130
111
|
const node = queue[i++];
|
|
131
112
|
sorted.push(node.id);
|
|
132
113
|
|
|
133
|
-
for (const dep of
|
|
114
|
+
for (const dep of passNodes) {
|
|
134
115
|
for (const input of dep.inputs) {
|
|
135
116
|
const producer = producers.get(input.id);
|
|
136
117
|
if (producer === node) {
|
|
137
|
-
const newDegree =
|
|
138
|
-
|
|
118
|
+
const newDegree = passInDegree.get(dep)! - 1;
|
|
119
|
+
passInDegree.set(dep, newDegree);
|
|
139
120
|
if (newDegree === 0) {
|
|
140
121
|
queue.push(dep);
|
|
141
122
|
}
|
|
@@ -156,11 +137,11 @@ export function formatGraph(info: GraphInspection, timing?: FrameTiming): string
|
|
|
156
137
|
const nodeMap = new Map(info.nodes.map((n) => [n.id, n]));
|
|
157
138
|
const timingMap = new Map(timing?.nodes.map((t) => [t.nodeId, t]));
|
|
158
139
|
|
|
159
|
-
for (const
|
|
160
|
-
const nodeIds = info.
|
|
140
|
+
for (const pass of PASS_ORDER) {
|
|
141
|
+
const nodeIds = info.byPass.get(pass) ?? [];
|
|
161
142
|
if (nodeIds.length === 0) continue;
|
|
162
143
|
|
|
163
|
-
lines.push(`[${
|
|
144
|
+
lines.push(`[${Pass[pass]}]`);
|
|
164
145
|
for (const id of nodeIds) {
|
|
165
146
|
const node = nodeMap.get(id)!;
|
|
166
147
|
const ins = node.inputs.map((r) => r.id).join(", ") || "none";
|
|
@@ -196,12 +177,13 @@ export function toDot(info: GraphInspection): string {
|
|
|
196
177
|
lines.push(" node [shape=box];");
|
|
197
178
|
lines.push("");
|
|
198
179
|
|
|
199
|
-
for (const
|
|
200
|
-
const nodeIds = info.
|
|
180
|
+
for (const pass of PASS_ORDER) {
|
|
181
|
+
const nodeIds = info.byPass.get(pass) ?? [];
|
|
201
182
|
if (nodeIds.length === 0) continue;
|
|
202
183
|
|
|
203
|
-
|
|
204
|
-
lines.push(`
|
|
184
|
+
const passName = Pass[pass];
|
|
185
|
+
lines.push(` subgraph cluster_${passName} {`);
|
|
186
|
+
lines.push(` label="${passName}";`);
|
|
205
187
|
for (const id of nodeIds) {
|
|
206
188
|
lines.push(` "${id}";`);
|
|
207
189
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export enum Pass {
|
|
2
|
+
BeforeOpaque,
|
|
3
|
+
Opaque,
|
|
4
|
+
AfterOpaque,
|
|
5
|
+
BeforeTransparent,
|
|
6
|
+
Transparent,
|
|
7
|
+
AfterTransparent,
|
|
8
|
+
BeforePost,
|
|
9
|
+
Post,
|
|
10
|
+
AfterPost,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const PASS_ORDER: Pass[] = [
|
|
14
|
+
Pass.BeforeOpaque,
|
|
15
|
+
Pass.Opaque,
|
|
16
|
+
Pass.AfterOpaque,
|
|
17
|
+
Pass.BeforeTransparent,
|
|
18
|
+
Pass.Transparent,
|
|
19
|
+
Pass.AfterTransparent,
|
|
20
|
+
Pass.BeforePost,
|
|
21
|
+
Pass.Post,
|
|
22
|
+
Pass.AfterPost,
|
|
23
|
+
];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { setTraits } from "../../core/component";
|
|
2
2
|
import { WorldTransform } from "../transforms";
|
|
3
3
|
import { clearColor } from "./forward";
|
|
4
|
-
import { perspective, orthographic, multiply, invert } from "./scene";
|
|
4
|
+
import { perspective, orthographic, multiply, invert, extractFrustumPlanes } from "./scene";
|
|
5
5
|
|
|
6
6
|
export const RenderMode = {
|
|
7
7
|
Raster: 0,
|
|
@@ -98,9 +98,14 @@ export function uploadCamera(
|
|
|
98
98
|
|
|
99
99
|
device.queue.writeBuffer(buffer, 0, viewProj as Float32Array<ArrayBuffer>);
|
|
100
100
|
device.queue.writeBuffer(buffer, 64, world as Float32Array<ArrayBuffer>);
|
|
101
|
+
|
|
102
|
+
const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
101
103
|
device.queue.writeBuffer(
|
|
102
104
|
buffer,
|
|
103
105
|
176,
|
|
104
|
-
new Float32Array([Camera.mode[eid], Camera.size[eid], width, height])
|
|
106
|
+
new Float32Array([Camera.mode[eid], Camera.size[eid], width / dpr, height / dpr])
|
|
105
107
|
);
|
|
108
|
+
|
|
109
|
+
const planes = extractFrustumPlanes(viewProj);
|
|
110
|
+
device.queue.writeBuffer(buffer, 192, planes as Float32Array<ArrayBuffer>);
|
|
106
111
|
}
|