@multiplekex/shallot 0.1.12 → 0.2.0
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 +3 -4
- package/src/core/builder.ts +71 -32
- package/src/core/component.ts +25 -11
- package/src/core/index.ts +14 -13
- package/src/core/math.ts +135 -0
- package/src/core/runtime.ts +0 -1
- package/src/core/state.ts +9 -68
- package/src/core/xml.ts +381 -265
- package/src/editor/format.ts +5 -0
- package/src/editor/index.ts +101 -0
- package/src/extras/arrows/index.ts +28 -69
- package/src/extras/gradient/index.ts +36 -52
- package/src/extras/lines/index.ts +51 -122
- package/src/extras/orbit/index.ts +40 -15
- package/src/extras/text/font.ts +546 -0
- package/src/extras/text/index.ts +158 -204
- package/src/extras/text/sdf.ts +429 -0
- package/src/standard/activity/index.ts +172 -0
- package/src/standard/compute/graph.ts +23 -23
- package/src/standard/compute/index.ts +76 -61
- package/src/standard/defaults.ts +8 -5
- package/src/standard/index.ts +1 -0
- package/src/standard/input/index.ts +30 -19
- package/src/standard/loading/index.ts +18 -13
- package/src/standard/render/bvh/blas.ts +752 -0
- package/src/standard/render/bvh/radix.ts +476 -0
- package/src/standard/render/bvh/structs.ts +167 -0
- package/src/standard/render/bvh/tlas.ts +886 -0
- package/src/standard/render/bvh/traverse.ts +467 -0
- package/src/standard/render/camera.ts +302 -27
- package/src/standard/render/data.ts +93 -0
- package/src/standard/render/depth.ts +117 -0
- package/src/standard/render/forward/index.ts +259 -0
- package/src/standard/render/forward/raster.ts +228 -0
- package/src/standard/render/index.ts +443 -70
- package/src/standard/render/indirect.ts +40 -0
- package/src/standard/render/instance.ts +214 -0
- package/src/standard/render/intersection.ts +72 -0
- package/src/standard/render/light.ts +16 -16
- package/src/standard/render/mesh/index.ts +67 -75
- package/src/standard/render/mesh/unified.ts +96 -0
- package/src/standard/render/{transparent.ts → overlay.ts} +14 -15
- package/src/standard/render/pass.ts +10 -4
- package/src/standard/render/postprocess.ts +142 -64
- package/src/standard/render/ray.ts +61 -0
- package/src/standard/render/scene.ts +38 -164
- package/src/standard/render/shaders.ts +484 -0
- package/src/standard/render/surface/compile.ts +3 -10
- package/src/standard/render/surface/index.ts +60 -30
- package/src/standard/render/surface/noise.ts +45 -0
- package/src/standard/render/surface/structs.ts +60 -19
- package/src/standard/render/surface/wgsl.ts +573 -0
- package/src/standard/render/triangle.ts +84 -0
- package/src/standard/transforms/index.ts +4 -6
- package/src/standard/tween/index.ts +10 -1
- package/src/standard/tween/sequence.ts +24 -16
- package/src/standard/tween/tween.ts +67 -16
- package/src/core/types.ts +0 -37
- package/src/standard/compute/inspect.ts +0 -201
- package/src/standard/compute/pass.ts +0 -23
- package/src/standard/compute/timing.ts +0 -139
- package/src/standard/render/forward.ts +0 -273
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import type { NodeId } from "./graph";
|
|
2
|
-
|
|
3
|
-
export interface NodeTiming {
|
|
4
|
-
readonly nodeId: NodeId;
|
|
5
|
-
readonly cpuMs: number;
|
|
6
|
-
readonly gpuNs?: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface FrameTiming {
|
|
10
|
-
readonly frameIndex: number;
|
|
11
|
-
readonly nodes: readonly NodeTiming[];
|
|
12
|
-
readonly totalCpuMs: number;
|
|
13
|
-
readonly totalGpuNs?: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface TimingConfig {
|
|
17
|
-
readonly enabled: boolean;
|
|
18
|
-
readonly gpuTimestamps: boolean;
|
|
19
|
-
readonly historySize: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface TimingState {
|
|
23
|
-
readonly config: TimingConfig;
|
|
24
|
-
readonly history: FrameTiming[];
|
|
25
|
-
readonly querySet?: GPUQuerySet;
|
|
26
|
-
readonly resolveBuffer?: GPUBuffer;
|
|
27
|
-
readonly readBuffer?: GPUBuffer;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function supportsTimestampQuery(device: GPUDevice): boolean {
|
|
31
|
-
return device.features.has("timestamp-query");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function createTimingState(
|
|
35
|
-
device: GPUDevice,
|
|
36
|
-
maxNodes: number,
|
|
37
|
-
historySize = 60
|
|
38
|
-
): TimingState {
|
|
39
|
-
const gpuTimestamps = supportsTimestampQuery(device);
|
|
40
|
-
|
|
41
|
-
let querySet: GPUQuerySet | undefined;
|
|
42
|
-
let resolveBuffer: GPUBuffer | undefined;
|
|
43
|
-
let readBuffer: GPUBuffer | undefined;
|
|
44
|
-
|
|
45
|
-
if (gpuTimestamps) {
|
|
46
|
-
const queryCount = maxNodes * 2;
|
|
47
|
-
querySet = device.createQuerySet({
|
|
48
|
-
label: "timing-queries",
|
|
49
|
-
type: "timestamp",
|
|
50
|
-
count: queryCount,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
resolveBuffer = device.createBuffer({
|
|
54
|
-
label: "timing-resolve",
|
|
55
|
-
size: queryCount * 8,
|
|
56
|
-
usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
readBuffer = device.createBuffer({
|
|
60
|
-
label: "timing-read",
|
|
61
|
-
size: queryCount * 8,
|
|
62
|
-
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
config: { enabled: true, gpuTimestamps, historySize },
|
|
68
|
-
history: [],
|
|
69
|
-
querySet,
|
|
70
|
-
resolveBuffer,
|
|
71
|
-
readBuffer,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export async function readTimestamps(
|
|
76
|
-
device: GPUDevice,
|
|
77
|
-
timing: TimingState,
|
|
78
|
-
nodeCount: number
|
|
79
|
-
): Promise<BigUint64Array | null> {
|
|
80
|
-
if (!timing.querySet || !timing.resolveBuffer || !timing.readBuffer) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const encoder = device.createCommandEncoder();
|
|
85
|
-
encoder.resolveQuerySet(timing.querySet, 0, nodeCount * 2, timing.resolveBuffer, 0);
|
|
86
|
-
encoder.copyBufferToBuffer(timing.resolveBuffer, 0, timing.readBuffer, 0, nodeCount * 2 * 8);
|
|
87
|
-
device.queue.submit([encoder.finish()]);
|
|
88
|
-
|
|
89
|
-
await timing.readBuffer.mapAsync(GPUMapMode.READ);
|
|
90
|
-
const data = new BigUint64Array(timing.readBuffer.getMappedRange().slice(0));
|
|
91
|
-
timing.readBuffer.unmap();
|
|
92
|
-
|
|
93
|
-
return data;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function disposeTimingState(timing: TimingState): void {
|
|
97
|
-
timing.querySet?.destroy();
|
|
98
|
-
timing.resolveBuffer?.destroy();
|
|
99
|
-
timing.readBuffer?.destroy();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface TimingCollector {
|
|
103
|
-
readonly nodeTimings: Map<NodeId, { start: number; end: number }>;
|
|
104
|
-
beginNode(nodeId: NodeId): void;
|
|
105
|
-
endNode(nodeId: NodeId): void;
|
|
106
|
-
finish(frameIndex: number): FrameTiming;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function createTimingCollector(): TimingCollector {
|
|
110
|
-
const nodeTimings = new Map<NodeId, { start: number; end: number }>();
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
nodeTimings,
|
|
114
|
-
|
|
115
|
-
beginNode(nodeId: NodeId): void {
|
|
116
|
-
nodeTimings.set(nodeId, { start: performance.now(), end: 0 });
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
endNode(nodeId: NodeId): void {
|
|
120
|
-
const timing = nodeTimings.get(nodeId);
|
|
121
|
-
if (timing) {
|
|
122
|
-
timing.end = performance.now();
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
finish(frameIndex: number): FrameTiming {
|
|
127
|
-
const nodes: NodeTiming[] = [];
|
|
128
|
-
let totalCpuMs = 0;
|
|
129
|
-
|
|
130
|
-
for (const [nodeId, timing] of nodeTimings) {
|
|
131
|
-
const cpuMs = timing.end - timing.start;
|
|
132
|
-
totalCpuMs += cpuMs;
|
|
133
|
-
nodes.push({ nodeId, cpuMs });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return { frameIndex, nodes, totalCpuMs };
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
}
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import type { State } from "../../core";
|
|
2
|
-
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
3
|
-
import type { ShapeBatch } from "./mesh";
|
|
4
|
-
import { DEPTH_FORMAT, ENTITY_ID_FORMAT } from "./scene";
|
|
5
|
-
import { getSurface, compileSurface, SurfaceType } from "./surface";
|
|
6
|
-
import { WGSL_STRUCTS } from "./surface/structs";
|
|
7
|
-
import { Pass, getDrawsByPass, type DrawContext } from "./pass";
|
|
8
|
-
|
|
9
|
-
export const INDIRECT_SIZE = 20;
|
|
10
|
-
|
|
11
|
-
export interface IndirectArgs {
|
|
12
|
-
indexCount: number;
|
|
13
|
-
instanceCount: number;
|
|
14
|
-
firstIndex: number;
|
|
15
|
-
baseVertex: number;
|
|
16
|
-
firstInstance: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function createIndirectBuffer(device: GPUDevice, slotCount: number): GPUBuffer {
|
|
20
|
-
return device.createBuffer({
|
|
21
|
-
label: "indirect",
|
|
22
|
-
size: slotCount * INDIRECT_SIZE,
|
|
23
|
-
usage:
|
|
24
|
-
GPUBufferUsage.INDIRECT |
|
|
25
|
-
GPUBufferUsage.STORAGE |
|
|
26
|
-
GPUBufferUsage.COPY_DST |
|
|
27
|
-
GPUBufferUsage.COPY_SRC,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function writeIndirect(
|
|
32
|
-
device: GPUDevice,
|
|
33
|
-
buffer: GPUBuffer,
|
|
34
|
-
slot: number,
|
|
35
|
-
args: IndirectArgs
|
|
36
|
-
): void {
|
|
37
|
-
const offset = slot * INDIRECT_SIZE;
|
|
38
|
-
const data = new ArrayBuffer(INDIRECT_SIZE);
|
|
39
|
-
const view = new DataView(data);
|
|
40
|
-
|
|
41
|
-
view.setUint32(0, args.indexCount, true);
|
|
42
|
-
view.setUint32(4, args.instanceCount, true);
|
|
43
|
-
view.setUint32(8, args.firstIndex, true);
|
|
44
|
-
view.setInt32(12, args.baseVertex, true);
|
|
45
|
-
view.setUint32(16, args.firstInstance, true);
|
|
46
|
-
|
|
47
|
-
device.queue.writeBuffer(buffer, offset, data);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const litShader = /* wgsl */ `
|
|
51
|
-
${WGSL_STRUCTS}
|
|
52
|
-
|
|
53
|
-
@vertex
|
|
54
|
-
fn vs(input: VertexInput) -> VertexOutput {
|
|
55
|
-
let eid = entityIds[input.instance];
|
|
56
|
-
let world = matrices[eid];
|
|
57
|
-
let scaledPos = input.position * sizes[eid].xyz;
|
|
58
|
-
let worldPos = world * vec4<f32>(scaledPos, 1.0);
|
|
59
|
-
let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
|
|
60
|
-
_ = shapes[eid]; // Keep binding alive
|
|
61
|
-
|
|
62
|
-
var output: VertexOutput;
|
|
63
|
-
output.position = scene.viewProj * worldPos;
|
|
64
|
-
output.color = colors[eid];
|
|
65
|
-
output.worldNormal = worldNormal;
|
|
66
|
-
output.entityId = eid;
|
|
67
|
-
output.worldPos = worldPos.xyz;
|
|
68
|
-
return output;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@fragment
|
|
72
|
-
fn fs(input: VertexOutput) -> FragmentOutput {
|
|
73
|
-
let eid = input.entityId;
|
|
74
|
-
let pbrData = pbr[eid];
|
|
75
|
-
let emissionData = emission[eid];
|
|
76
|
-
let normal = normalize(input.worldNormal);
|
|
77
|
-
let NdotL = max(dot(normal, -scene.sunDirection.xyz), 0.0);
|
|
78
|
-
|
|
79
|
-
let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
|
|
80
|
-
let diffuse = scene.sunColor.rgb * NdotL;
|
|
81
|
-
let diffuseFactor = 1.0 - pbrData.y * 0.9;
|
|
82
|
-
let lighting = ambient + diffuse * diffuseFactor;
|
|
83
|
-
|
|
84
|
-
var output: FragmentOutput;
|
|
85
|
-
output.color = vec4<f32>(input.color.rgb * lighting + emissionData.rgb * emissionData.a, input.color.a);
|
|
86
|
-
output.entityId = input.entityId;
|
|
87
|
-
return output;
|
|
88
|
-
}
|
|
89
|
-
`;
|
|
90
|
-
|
|
91
|
-
export function createForwardPipeline(
|
|
92
|
-
device: GPUDevice,
|
|
93
|
-
format: GPUTextureFormat,
|
|
94
|
-
shaderCode: string
|
|
95
|
-
): GPURenderPipeline {
|
|
96
|
-
const module = device.createShaderModule({ code: shaderCode });
|
|
97
|
-
|
|
98
|
-
return device.createRenderPipeline({
|
|
99
|
-
layout: "auto",
|
|
100
|
-
vertex: {
|
|
101
|
-
module,
|
|
102
|
-
entryPoint: "vs",
|
|
103
|
-
buffers: [
|
|
104
|
-
{
|
|
105
|
-
arrayStride: 24,
|
|
106
|
-
attributes: [
|
|
107
|
-
{ shaderLocation: 0, offset: 0, format: "float32x3" as const },
|
|
108
|
-
{ shaderLocation: 1, offset: 12, format: "float32x3" as const },
|
|
109
|
-
],
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
},
|
|
113
|
-
fragment: {
|
|
114
|
-
module,
|
|
115
|
-
entryPoint: "fs",
|
|
116
|
-
targets: [{ format }, { format: ENTITY_ID_FORMAT, writeMask: 0x1 }],
|
|
117
|
-
},
|
|
118
|
-
primitive: {
|
|
119
|
-
topology: "triangle-list",
|
|
120
|
-
cullMode: "back",
|
|
121
|
-
},
|
|
122
|
-
depthStencil: {
|
|
123
|
-
format: DEPTH_FORMAT,
|
|
124
|
-
depthWriteEnabled: true,
|
|
125
|
-
depthCompare: "less",
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function getOrCreatePipeline(
|
|
131
|
-
device: GPUDevice,
|
|
132
|
-
format: GPUTextureFormat,
|
|
133
|
-
surfaceId: number,
|
|
134
|
-
pipelineCache: Map<number, GPURenderPipeline>
|
|
135
|
-
): GPURenderPipeline {
|
|
136
|
-
let pipeline = pipelineCache.get(surfaceId);
|
|
137
|
-
if (!pipeline) {
|
|
138
|
-
const data = getSurface(surfaceId) ?? getSurface(SurfaceType.Default)!;
|
|
139
|
-
const code = compileSurface(data);
|
|
140
|
-
pipeline = createForwardPipeline(device, format, code);
|
|
141
|
-
pipelineCache.set(surfaceId, pipeline);
|
|
142
|
-
}
|
|
143
|
-
return pipeline;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export const clearColor = { r: 0.1, g: 0.1, b: 0.1, a: 1 };
|
|
147
|
-
|
|
148
|
-
export interface ForwardConfig {
|
|
149
|
-
state: State;
|
|
150
|
-
scene: GPUBuffer;
|
|
151
|
-
matrices: GPUBuffer;
|
|
152
|
-
colors: GPUBuffer;
|
|
153
|
-
sizes: GPUBuffer;
|
|
154
|
-
shapes: GPUBuffer;
|
|
155
|
-
pbr: GPUBuffer;
|
|
156
|
-
emission: GPUBuffer;
|
|
157
|
-
indirect: GPUBuffer;
|
|
158
|
-
batches: Map<string, ShapeBatch>;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function createForwardNode(config: ForwardConfig): ComputeNode {
|
|
162
|
-
const pipelineCache = new Map<number, GPURenderPipeline>();
|
|
163
|
-
const bindGroups = new Map<string, GPUBindGroup>();
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
id: "forward",
|
|
167
|
-
pass: Pass.Opaque,
|
|
168
|
-
inputs: [],
|
|
169
|
-
outputs: [
|
|
170
|
-
{ id: "scene", access: "write" },
|
|
171
|
-
{ id: "depth", access: "write" },
|
|
172
|
-
{ id: "entityId", access: "write" },
|
|
173
|
-
],
|
|
174
|
-
|
|
175
|
-
execute(ctx: ExecutionContext) {
|
|
176
|
-
const { device, encoder, format, context } = ctx;
|
|
177
|
-
const targetView = ctx.getTextureView("scene") ?? ctx.canvasView;
|
|
178
|
-
const depthView = ctx.getTextureView("depth")!;
|
|
179
|
-
const entityIdView = ctx.getTextureView("entityId")!;
|
|
180
|
-
const maskView = ctx.getTextureView("mask")!;
|
|
181
|
-
|
|
182
|
-
const passCtx: DrawContext = {
|
|
183
|
-
device,
|
|
184
|
-
encoder,
|
|
185
|
-
format,
|
|
186
|
-
width: context.canvas.width,
|
|
187
|
-
height: context.canvas.height,
|
|
188
|
-
sceneView: targetView,
|
|
189
|
-
depthView,
|
|
190
|
-
entityIdView,
|
|
191
|
-
maskView,
|
|
192
|
-
canvasView: ctx.canvasView,
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const beforeOpaques = getDrawsByPass(config.state, Pass.BeforeOpaque);
|
|
196
|
-
for (const contributor of beforeOpaques) {
|
|
197
|
-
contributor.execute(passCtx);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const pass = encoder.beginRenderPass({
|
|
201
|
-
colorAttachments: [
|
|
202
|
-
{
|
|
203
|
-
view: targetView,
|
|
204
|
-
clearValue: clearColor,
|
|
205
|
-
loadOp: "clear" as const,
|
|
206
|
-
storeOp: "store" as const,
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
view: entityIdView,
|
|
210
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
211
|
-
loadOp: "clear" as const,
|
|
212
|
-
storeOp: "store" as const,
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
depthStencilAttachment: {
|
|
216
|
-
view: depthView,
|
|
217
|
-
depthClearValue: 1.0,
|
|
218
|
-
depthLoadOp: "clear" as const,
|
|
219
|
-
depthStoreOp: "store" as const,
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
let currentSurfaceId = -1;
|
|
224
|
-
let currentPipeline: GPURenderPipeline | null = null;
|
|
225
|
-
|
|
226
|
-
for (const batch of config.batches.values()) {
|
|
227
|
-
if (batch.count === 0) continue;
|
|
228
|
-
|
|
229
|
-
if (batch.surface !== currentSurfaceId) {
|
|
230
|
-
currentSurfaceId = batch.surface;
|
|
231
|
-
currentPipeline = getOrCreatePipeline(
|
|
232
|
-
device,
|
|
233
|
-
format,
|
|
234
|
-
currentSurfaceId,
|
|
235
|
-
pipelineCache
|
|
236
|
-
);
|
|
237
|
-
pass.setPipeline(currentPipeline);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const bindGroupKey = `${batch.index}:${batch.surface}`;
|
|
241
|
-
let bindGroup = bindGroups.get(bindGroupKey);
|
|
242
|
-
if (!bindGroup) {
|
|
243
|
-
bindGroup = device.createBindGroup({
|
|
244
|
-
layout: currentPipeline!.getBindGroupLayout(0),
|
|
245
|
-
entries: [
|
|
246
|
-
{ binding: 0, resource: { buffer: config.scene } },
|
|
247
|
-
{ binding: 1, resource: { buffer: batch.entityIds } },
|
|
248
|
-
{ binding: 2, resource: { buffer: config.matrices } },
|
|
249
|
-
{ binding: 3, resource: { buffer: config.colors } },
|
|
250
|
-
{ binding: 4, resource: { buffer: config.sizes } },
|
|
251
|
-
{ binding: 5, resource: { buffer: config.pbr } },
|
|
252
|
-
{ binding: 6, resource: { buffer: config.emission } },
|
|
253
|
-
{ binding: 7, resource: { buffer: config.shapes } },
|
|
254
|
-
],
|
|
255
|
-
});
|
|
256
|
-
bindGroups.set(bindGroupKey, bindGroup);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
pass.setBindGroup(0, bindGroup);
|
|
260
|
-
pass.setVertexBuffer(0, batch.buffers.vertex);
|
|
261
|
-
pass.setIndexBuffer(batch.buffers.index, "uint16");
|
|
262
|
-
pass.drawIndexedIndirect(config.indirect, batch.index * INDIRECT_SIZE);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
pass.end();
|
|
266
|
-
|
|
267
|
-
const afterOpaques = getDrawsByPass(config.state, Pass.AfterOpaque);
|
|
268
|
-
for (const contributor of afterOpaques) {
|
|
269
|
-
contributor.execute(passCtx);
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
};
|
|
273
|
-
}
|