@forgeax/engine-render 0.1.6 → 0.1.7
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/README.md +139 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +7 -0
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/authoring.d.ts +1 -0
- package/dist/authoring.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-QC4VW3XJ.mjs → chunk-HCOGPHKS.mjs} +17 -3
- package/dist/chunk-HCOGPHKS.mjs.map +1 -0
- package/dist/chunk-YEJIUW7D.mjs +1152 -0
- package/dist/chunk-YEJIUW7D.mjs.map +1 -0
- package/dist/components/__tests__/points-lines-admission.unit.test.d.ts +2 -0
- package/dist/components/__tests__/points-lines-admission.unit.test.d.ts.map +1 -0
- package/dist/components/__tests__/points-lines-schema.unit.test.d.ts +2 -0
- package/dist/components/__tests__/points-lines-schema.unit.test.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/lines.d.ts +5 -0
- package/dist/components/lines.d.ts.map +1 -0
- package/dist/components/points.d.ts +15 -0
- package/dist/components/points.d.ts.map +1 -0
- package/dist/construct-renderer.mjs +952 -497
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/errors/render.d.ts +82 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +5 -1
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +4 -2
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +3 -0
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +2 -0
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/points-lines/__tests__/bounds.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/bounds.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts +2 -0
- package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/inspection.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/inspection.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts +2 -0
- package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/points-lines.bench.d.ts +2 -0
- package/dist/points-lines/__tests__/points-lines.bench.d.ts.map +1 -0
- package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts +2 -0
- package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/snapshot.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/snapshot.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts.map +1 -0
- package/dist/points-lines/admission.d.ts +36 -0
- package/dist/points-lines/admission.d.ts.map +1 -0
- package/dist/points-lines/bounds.d.ts +8 -0
- package/dist/points-lines/bounds.d.ts.map +1 -0
- package/dist/points-lines/expansion-cache.d.ts +28 -0
- package/dist/points-lines/expansion-cache.d.ts.map +1 -0
- package/dist/points-lines/inspection.d.ts +53 -0
- package/dist/points-lines/inspection.d.ts.map +1 -0
- package/dist/points-lines/prepare.d.ts +67 -0
- package/dist/points-lines/prepare.d.ts.map +1 -0
- package/dist/points-lines/record.d.ts +60 -0
- package/dist/points-lines/record.d.ts.map +1 -0
- package/dist/points-lines/snapshot.d.ts +49 -0
- package/dist/points-lines/snapshot.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame.d.ts +1 -1
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +7 -0
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +17 -0
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +6 -0
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/render-contract.d.ts +10 -2
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +3 -0
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +4 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +3 -0
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +7 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/package.json +19 -19
- package/src/__tests__/authoring-path.unit.test.ts +9 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +12 -2
- package/src/__tests__/material-cooked-projection.unit.test.ts +29 -7
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +28 -28
- package/src/__tests__/render-error-code-owner.test-d.ts +6 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +19 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +38 -0
- package/src/assembly/factory.ts +72 -3
- package/src/assembly/material/assembly.ts +2 -5
- package/src/authoring.ts +1 -0
- package/src/components/__tests__/points-lines-admission.unit.test.ts +219 -0
- package/src/components/__tests__/points-lines-schema.unit.test.ts +56 -0
- package/src/components/index.ts +7 -0
- package/src/components/lines.ts +18 -0
- package/src/components/points.ts +40 -0
- package/src/errors/render.ts +144 -1
- package/src/features/render-graph-raster.ts +22 -3
- package/src/gpu-driven/production-raster.ts +6 -1
- package/src/index.ts +17 -0
- package/src/inspection-types.ts +4 -0
- package/src/pbr-pipeline.ts +8 -1
- package/src/plugin.ts +4 -0
- package/src/points-lines/__tests__/bounds.unit.test.ts +21 -0
- package/src/points-lines/__tests__/cache-recovery.unit.test.ts +157 -0
- package/src/points-lines/__tests__/inspection-lanes.integration.test.ts +146 -0
- package/src/points-lines/__tests__/inspection.unit.test.ts +173 -0
- package/src/points-lines/__tests__/lane-structural.integration.test.ts +122 -0
- package/src/points-lines/__tests__/points-lines.bench.ts +400 -0
- package/src/points-lines/__tests__/raster-lane.integration.test.ts +44 -0
- package/src/points-lines/__tests__/snapshot.unit.test.ts +101 -0
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +73 -0
- package/src/points-lines/admission.ts +238 -0
- package/src/points-lines/bounds.ts +23 -0
- package/src/points-lines/expansion-cache.ts +215 -0
- package/src/points-lines/inspection.ts +97 -0
- package/src/points-lines/prepare.ts +177 -0
- package/src/points-lines/record.ts +125 -0
- package/src/points-lines/snapshot.ts +130 -0
- package/src/record/frame-lighting.ts +14 -2
- package/src/record/frame.ts +12 -1
- package/src/record/main-pass-geometry.ts +92 -0
- package/src/record/main-pass-sprite-draws.ts +1 -1
- package/src/record/main-pass.ts +6 -3
- package/src/record/render-context.ts +20 -0
- package/src/record/shadow-pass.ts +16 -5
- package/src/record/skybox-post-pass.ts +2 -1
- package/src/record/typed-frame-graph.ts +17 -16
- package/src/record/view-ubo.ts +47 -0
- package/src/render-contract.ts +13 -2
- package/src/render-system-extract.ts +71 -8
- package/src/render-system.ts +304 -2
- package/src/scene/render-scene-types.ts +3 -0
- package/src/scene/render-scene.ts +57 -2
- package/dist/chunk-QC4VW3XJ.mjs.map +0 -1
- package/dist/chunk-SCRSAK5B.mjs +0 -480
- package/dist/chunk-SCRSAK5B.mjs.map +0 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { MeshAsset } from '@forgeax/engine-types';
|
|
2
|
+
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
3
|
+
import { PointsLinesPrepareFailedError } from '../errors/render';
|
|
4
|
+
import type { PointsLinesExpandedGeometry, PointsLinesExpansionCache } from './expansion-cache';
|
|
5
|
+
import {
|
|
6
|
+
createPointsLinesLaneContract,
|
|
7
|
+
type PointsLinesBackend,
|
|
8
|
+
type PointsLinesLane,
|
|
9
|
+
type PointsLinesLaneContract,
|
|
10
|
+
} from './record';
|
|
11
|
+
import type { PointsLinesRetainedSnapshot } from './snapshot';
|
|
12
|
+
|
|
13
|
+
export interface PointsLinesGpuResourceAdapter<Resource> {
|
|
14
|
+
create(geometry: PointsLinesExpandedGeometry): Result<Resource, Error>;
|
|
15
|
+
upload(resource: Resource, geometry: PointsLinesExpandedGeometry): Result<number, Error>;
|
|
16
|
+
validate(resource: Resource, geometry: PointsLinesExpandedGeometry): Result<void, Error>;
|
|
17
|
+
destroy(resource: Resource): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PointsLinesPreparedResource<Resource> {
|
|
21
|
+
readonly resource: Resource;
|
|
22
|
+
readonly geometry: PointsLinesExpandedGeometry;
|
|
23
|
+
readonly snapshot: PointsLinesRetainedSnapshot;
|
|
24
|
+
readonly uploadedBytes: number;
|
|
25
|
+
readonly lastKnownGood: true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PointsLinesPreparationOptions<Resource> {
|
|
29
|
+
readonly cache: PointsLinesExpansionCache;
|
|
30
|
+
readonly adapter: PointsLinesGpuResourceAdapter<Resource>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PointsLinesLanePreparationAdapter<Resource> {
|
|
34
|
+
readonly contract: PointsLinesLaneContract;
|
|
35
|
+
prepare(
|
|
36
|
+
snapshot: PointsLinesRetainedSnapshot,
|
|
37
|
+
mesh: MeshAsset,
|
|
38
|
+
): Result<PointsLinesPreparedResource<Resource>, PointsLinesPrepareFailedError>;
|
|
39
|
+
resetForDeviceLoss(): void;
|
|
40
|
+
lastKnownGood(): PointsLinesPreparedResource<Resource> | undefined;
|
|
41
|
+
inspect(): PointsLinesPreparationInspection<Resource>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface PointsLinesPreparationInspection<Resource> {
|
|
45
|
+
readonly status: 'empty' | 'resident' | 'rebuild-pending';
|
|
46
|
+
readonly lastKnownGood: boolean;
|
|
47
|
+
readonly liveResource: Resource | undefined;
|
|
48
|
+
readonly liveBytes: number;
|
|
49
|
+
readonly candidateBytes: number;
|
|
50
|
+
readonly cacheEntries: number;
|
|
51
|
+
readonly uploads: number;
|
|
52
|
+
readonly rebuilds: number;
|
|
53
|
+
readonly failures: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Owns one atomic candidate/LKG pair for a Points/Lines render projection.
|
|
58
|
+
* GPU resources are supplied by the existing DeviceScope adapter; this class
|
|
59
|
+
* keeps no backend handle or second resource ledger.
|
|
60
|
+
*/
|
|
61
|
+
export class PointsLinesPreparation<Resource> {
|
|
62
|
+
private live: PointsLinesPreparedResource<Resource> | undefined;
|
|
63
|
+
private pendingCandidateBytes = 0;
|
|
64
|
+
private status: 'empty' | 'resident' | 'rebuild-pending' = 'empty';
|
|
65
|
+
private uploads = 0;
|
|
66
|
+
private rebuilds = 0;
|
|
67
|
+
private failures = 0;
|
|
68
|
+
|
|
69
|
+
constructor(private readonly options: PointsLinesPreparationOptions<Resource>) {}
|
|
70
|
+
|
|
71
|
+
prepare(
|
|
72
|
+
snapshot: PointsLinesRetainedSnapshot,
|
|
73
|
+
mesh: MeshAsset,
|
|
74
|
+
): Result<PointsLinesPreparedResource<Resource>, PointsLinesPrepareFailedError> {
|
|
75
|
+
const geometry = this.options.cache.getOrCreate(snapshot, mesh);
|
|
76
|
+
if (this.live?.geometry.key === geometry.key) {
|
|
77
|
+
this.live = {
|
|
78
|
+
...this.live,
|
|
79
|
+
snapshot,
|
|
80
|
+
uploadedBytes: 0,
|
|
81
|
+
};
|
|
82
|
+
this.status = 'resident';
|
|
83
|
+
return ok(this.live);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.pendingCandidateBytes = geometry.derivedBytes;
|
|
87
|
+
const created = this.options.adapter.create(geometry);
|
|
88
|
+
if (!created.ok) return this.fail(snapshot, created.error);
|
|
89
|
+
const candidate = created.value;
|
|
90
|
+
const uploaded = this.options.adapter.upload(candidate, geometry);
|
|
91
|
+
if (!uploaded.ok) {
|
|
92
|
+
this.options.adapter.destroy(candidate);
|
|
93
|
+
return this.fail(snapshot, uploaded.error);
|
|
94
|
+
}
|
|
95
|
+
this.uploads += 1;
|
|
96
|
+
const validated = this.options.adapter.validate(candidate, geometry);
|
|
97
|
+
if (!validated.ok) {
|
|
98
|
+
this.options.adapter.destroy(candidate);
|
|
99
|
+
return this.fail(snapshot, validated.error);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const previous = this.live;
|
|
103
|
+
this.live = {
|
|
104
|
+
resource: candidate,
|
|
105
|
+
geometry,
|
|
106
|
+
snapshot,
|
|
107
|
+
uploadedBytes: uploaded.value,
|
|
108
|
+
lastKnownGood: true,
|
|
109
|
+
};
|
|
110
|
+
this.pendingCandidateBytes = 0;
|
|
111
|
+
this.status = 'resident';
|
|
112
|
+
this.rebuilds += 1;
|
|
113
|
+
if (previous !== undefined) this.options.adapter.destroy(previous.resource);
|
|
114
|
+
return ok(this.live);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
resetForDeviceLoss(): void {
|
|
118
|
+
if (this.live !== undefined) this.options.adapter.destroy(this.live.resource);
|
|
119
|
+
this.live = undefined;
|
|
120
|
+
this.pendingCandidateBytes = 0;
|
|
121
|
+
this.status = 'rebuild-pending';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
lastKnownGood(): PointsLinesPreparedResource<Resource> | undefined {
|
|
125
|
+
return this.live;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
inspect(): PointsLinesPreparationInspection<Resource> {
|
|
129
|
+
return {
|
|
130
|
+
status: this.status,
|
|
131
|
+
lastKnownGood: this.live?.lastKnownGood ?? false,
|
|
132
|
+
liveResource: this.live?.resource,
|
|
133
|
+
liveBytes: this.live?.geometry.derivedBytes ?? 0,
|
|
134
|
+
candidateBytes: this.pendingCandidateBytes,
|
|
135
|
+
cacheEntries: this.options.cache.size,
|
|
136
|
+
uploads: this.uploads,
|
|
137
|
+
rebuilds: this.rebuilds,
|
|
138
|
+
failures: this.failures,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private fail(
|
|
143
|
+
snapshot: PointsLinesRetainedSnapshot,
|
|
144
|
+
cause: Error,
|
|
145
|
+
): Result<never, PointsLinesPrepareFailedError> {
|
|
146
|
+
this.pendingCandidateBytes = 0;
|
|
147
|
+
this.failures += 1;
|
|
148
|
+
return err(
|
|
149
|
+
new PointsLinesPrepareFailedError({
|
|
150
|
+
owner: 'points-lines',
|
|
151
|
+
generation: snapshot.meshGeneration,
|
|
152
|
+
stage: 'prepare',
|
|
153
|
+
cause: cause.message,
|
|
154
|
+
lastKnownGood: this.live !== undefined,
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Binds the existing atomic preparation owner to a lane contract. It carries
|
|
162
|
+
* no RHI handle and does not add a second cache or recovery owner.
|
|
163
|
+
*/
|
|
164
|
+
export function createPointsLinesLanePreparationAdapter<Resource>(
|
|
165
|
+
lane: PointsLinesLane,
|
|
166
|
+
backend: PointsLinesBackend,
|
|
167
|
+
options: PointsLinesPreparationOptions<Resource>,
|
|
168
|
+
): PointsLinesLanePreparationAdapter<Resource> {
|
|
169
|
+
const preparation = new PointsLinesPreparation(options);
|
|
170
|
+
return {
|
|
171
|
+
contract: createPointsLinesLaneContract(lane, backend),
|
|
172
|
+
prepare: (snapshot, mesh) => preparation.prepare(snapshot, mesh),
|
|
173
|
+
resetForDeviceLoss: () => preparation.resetForDeviceLoss(),
|
|
174
|
+
lastKnownGood: () => preparation.lastKnownGood(),
|
|
175
|
+
inspect: () => preparation.inspect(),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { PointsLinesExpandedGeometry } from './expansion-cache';
|
|
2
|
+
import type { PointsLinesRetainedSnapshot } from './snapshot';
|
|
3
|
+
|
|
4
|
+
export const POINTS_LINES_MATERIAL_SHADER_ID = 'forgeax::points-lines';
|
|
5
|
+
|
|
6
|
+
export type PointsLinesLane = 'direct' | 'clustered' | 'cpu-webgl2';
|
|
7
|
+
export type PointsLinesBackend = 'webgpu' | 'wgpu-webgl2' | 'null';
|
|
8
|
+
|
|
9
|
+
export interface PointsLinesLaneContract {
|
|
10
|
+
readonly lane: PointsLinesLane;
|
|
11
|
+
readonly backend: PointsLinesBackend;
|
|
12
|
+
readonly authoring: 'shared';
|
|
13
|
+
readonly evidence: 'runtime' | 'structural-only';
|
|
14
|
+
readonly material: {
|
|
15
|
+
readonly source: 'MaterialAsset';
|
|
16
|
+
readonly shaderId: typeof POINTS_LINES_MATERIAL_SHADER_ID;
|
|
17
|
+
readonly shadingModel: 'unlit';
|
|
18
|
+
readonly clusteredLighting: false;
|
|
19
|
+
readonly lit: false;
|
|
20
|
+
};
|
|
21
|
+
readonly graph: {
|
|
22
|
+
readonly additionalAttachments: 0;
|
|
23
|
+
readonly additionalPasses: 0;
|
|
24
|
+
};
|
|
25
|
+
readonly shadowDrawCount: 0;
|
|
26
|
+
readonly capabilities: {
|
|
27
|
+
readonly compute: false;
|
|
28
|
+
readonly storage: false;
|
|
29
|
+
readonly indirect: false;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PointsLinesRecordPlan {
|
|
34
|
+
readonly lane: PointsLinesLane;
|
|
35
|
+
readonly backend: PointsLinesBackend;
|
|
36
|
+
readonly materialShaderId: typeof POINTS_LINES_MATERIAL_SHADER_ID;
|
|
37
|
+
readonly component: 'Points' | 'Lines';
|
|
38
|
+
readonly meshGeneration: number;
|
|
39
|
+
readonly materialGeneration: number;
|
|
40
|
+
readonly vertexCount: number;
|
|
41
|
+
readonly indexCount: number;
|
|
42
|
+
readonly drawCount: 0 | 1;
|
|
43
|
+
readonly shadowDrawCount: 0;
|
|
44
|
+
readonly conservativeMarginPx: number;
|
|
45
|
+
readonly graph: PointsLinesLaneContract['graph'];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Describes the shared lane boundary without selecting a backend in authoring.
|
|
50
|
+
* Every lane uses CPU-uploaded triangle expansion; capability flags therefore
|
|
51
|
+
* stay false, including on WebGPU, and the graph remains the existing pass.
|
|
52
|
+
*/
|
|
53
|
+
export function createPointsLinesLaneContract(
|
|
54
|
+
lane: PointsLinesLane,
|
|
55
|
+
backend: PointsLinesBackend,
|
|
56
|
+
): PointsLinesLaneContract {
|
|
57
|
+
return {
|
|
58
|
+
lane,
|
|
59
|
+
backend,
|
|
60
|
+
authoring: 'shared',
|
|
61
|
+
evidence: backend === 'null' ? 'structural-only' : 'runtime',
|
|
62
|
+
material: {
|
|
63
|
+
source: 'MaterialAsset',
|
|
64
|
+
shaderId: POINTS_LINES_MATERIAL_SHADER_ID,
|
|
65
|
+
shadingModel: 'unlit',
|
|
66
|
+
clusteredLighting: false,
|
|
67
|
+
lit: false,
|
|
68
|
+
},
|
|
69
|
+
graph: { additionalAttachments: 0, additionalPasses: 0 },
|
|
70
|
+
shadowDrawCount: 0,
|
|
71
|
+
capabilities: { compute: false, storage: false, indirect: false },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Turns one retained expansion into the bounded draw facts consumed by the
|
|
77
|
+
* existing record pass. The shader expands one quad per point or segment,
|
|
78
|
+
* so the limits are exactly four vertices and six indices per primitive.
|
|
79
|
+
*/
|
|
80
|
+
export function createPointsLinesRecordPlan(
|
|
81
|
+
snapshot: PointsLinesRetainedSnapshot,
|
|
82
|
+
geometry: PointsLinesExpandedGeometry,
|
|
83
|
+
contract: PointsLinesLaneContract,
|
|
84
|
+
): PointsLinesRecordPlan {
|
|
85
|
+
const primitiveCount = geometry.pointCount + geometry.segmentCount;
|
|
86
|
+
const visible = snapshot.visible && primitiveCount > 0;
|
|
87
|
+
const conservativeMarginPx =
|
|
88
|
+
snapshot.style?.kind === 'points'
|
|
89
|
+
? snapshot.style.sizePx * 0.5
|
|
90
|
+
: (snapshot.style?.widthPx ?? 0) * 0.5;
|
|
91
|
+
return {
|
|
92
|
+
lane: contract.lane,
|
|
93
|
+
backend: contract.backend,
|
|
94
|
+
materialShaderId: POINTS_LINES_MATERIAL_SHADER_ID,
|
|
95
|
+
component: geometry.component,
|
|
96
|
+
meshGeneration: snapshot.meshGeneration,
|
|
97
|
+
materialGeneration: snapshot.materialGeneration,
|
|
98
|
+
vertexCount: primitiveCount * 4,
|
|
99
|
+
indexCount: primitiveCount * 6,
|
|
100
|
+
drawCount: visible ? 1 : 0,
|
|
101
|
+
shadowDrawCount: 0,
|
|
102
|
+
conservativeMarginPx,
|
|
103
|
+
graph: contract.graph,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface PointsLinesLaneAdapter {
|
|
108
|
+
readonly contract: PointsLinesLaneContract;
|
|
109
|
+
createRecordPlan(
|
|
110
|
+
snapshot: PointsLinesRetainedSnapshot,
|
|
111
|
+
geometry: PointsLinesExpandedGeometry,
|
|
112
|
+
): PointsLinesRecordPlan;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function createPointsLinesLaneAdapter(
|
|
116
|
+
lane: PointsLinesLane,
|
|
117
|
+
backend: PointsLinesBackend,
|
|
118
|
+
): PointsLinesLaneAdapter {
|
|
119
|
+
const contract = createPointsLinesLaneContract(lane, backend);
|
|
120
|
+
return {
|
|
121
|
+
contract,
|
|
122
|
+
createRecordPlan: (snapshot, geometry) =>
|
|
123
|
+
createPointsLinesRecordPlan(snapshot, geometry, contract),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { PointShape } from '../components/points';
|
|
2
|
+
|
|
3
|
+
export interface PointsLinesViewport {
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly dpr: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type PointsLinesStyle =
|
|
10
|
+
| { readonly kind: 'points'; readonly sizePx: number; readonly shape: PointShape }
|
|
11
|
+
| { readonly kind: 'lines'; readonly widthPx: number };
|
|
12
|
+
|
|
13
|
+
export type PointsLinesInvalidation = 'none' | 'mesh' | 'material' | 'style' | 'view' | 'scene';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detached facts retained between frames for the Points/Lines owner.
|
|
17
|
+
*
|
|
18
|
+
* The snapshot deliberately contains no World, ECS row, AssetRegistry, GPU
|
|
19
|
+
* handle, or derived triangle data. Mesh and material generations are the
|
|
20
|
+
* producer-owned fences used by prepare; viewport and projection are the
|
|
21
|
+
* only camera-owned facts in this projection.
|
|
22
|
+
*/
|
|
23
|
+
export interface PointsLinesRetainedSnapshot {
|
|
24
|
+
readonly worldId: number;
|
|
25
|
+
readonly entityKey: number;
|
|
26
|
+
readonly component: 'Points' | 'Lines' | undefined;
|
|
27
|
+
readonly meshHandle: number;
|
|
28
|
+
readonly meshGeneration: number;
|
|
29
|
+
readonly materialHandle: number;
|
|
30
|
+
readonly materialGeneration: number;
|
|
31
|
+
readonly style: PointsLinesStyle | undefined;
|
|
32
|
+
readonly layer: number;
|
|
33
|
+
readonly sortKey: number | undefined;
|
|
34
|
+
readonly visible: boolean;
|
|
35
|
+
readonly sourceBounds: Float32Array;
|
|
36
|
+
readonly viewport: PointsLinesViewport;
|
|
37
|
+
readonly projection: Float32Array;
|
|
38
|
+
readonly dedicatedResourceBytes?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface PointsLinesSnapshotInput
|
|
42
|
+
extends Omit<
|
|
43
|
+
PointsLinesRetainedSnapshot,
|
|
44
|
+
'sourceBounds' | 'viewport' | 'projection' | 'sortKey'
|
|
45
|
+
> {
|
|
46
|
+
readonly sortKey?: number | undefined;
|
|
47
|
+
readonly sourceBounds: ArrayLike<number>;
|
|
48
|
+
readonly viewport: PointsLinesViewport;
|
|
49
|
+
readonly projection: ArrayLike<number>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createPointsLinesSnapshot(
|
|
53
|
+
input: PointsLinesSnapshotInput,
|
|
54
|
+
): PointsLinesRetainedSnapshot {
|
|
55
|
+
return {
|
|
56
|
+
worldId: input.worldId,
|
|
57
|
+
entityKey: input.entityKey,
|
|
58
|
+
component: input.component,
|
|
59
|
+
meshHandle: input.meshHandle,
|
|
60
|
+
meshGeneration: input.meshGeneration,
|
|
61
|
+
materialHandle: input.materialHandle,
|
|
62
|
+
materialGeneration: input.materialGeneration,
|
|
63
|
+
style: input.style === undefined ? undefined : { ...input.style },
|
|
64
|
+
layer: input.layer,
|
|
65
|
+
sortKey: input.sortKey,
|
|
66
|
+
visible: input.visible,
|
|
67
|
+
sourceBounds: new Float32Array(input.sourceBounds),
|
|
68
|
+
viewport: { ...input.viewport },
|
|
69
|
+
projection: new Float32Array(input.projection),
|
|
70
|
+
...(input.component === undefined ? { dedicatedResourceBytes: 0 } : {}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function comparePointsLinesSnapshots(
|
|
75
|
+
previous: PointsLinesRetainedSnapshot,
|
|
76
|
+
next: PointsLinesRetainedSnapshot,
|
|
77
|
+
): PointsLinesInvalidation {
|
|
78
|
+
if (
|
|
79
|
+
previous.meshHandle !== next.meshHandle ||
|
|
80
|
+
previous.meshGeneration !== next.meshGeneration ||
|
|
81
|
+
!sameArray(previous.sourceBounds, next.sourceBounds)
|
|
82
|
+
) {
|
|
83
|
+
return 'mesh';
|
|
84
|
+
}
|
|
85
|
+
if (
|
|
86
|
+
previous.materialHandle !== next.materialHandle ||
|
|
87
|
+
previous.materialGeneration !== next.materialGeneration
|
|
88
|
+
) {
|
|
89
|
+
return 'material';
|
|
90
|
+
}
|
|
91
|
+
if (!sameValue(previous.style, next.style)) return 'style';
|
|
92
|
+
if (
|
|
93
|
+
previous.viewport.width !== next.viewport.width ||
|
|
94
|
+
previous.viewport.height !== next.viewport.height ||
|
|
95
|
+
previous.viewport.dpr !== next.viewport.dpr ||
|
|
96
|
+
!sameArray(previous.projection, next.projection)
|
|
97
|
+
) {
|
|
98
|
+
return 'view';
|
|
99
|
+
}
|
|
100
|
+
if (
|
|
101
|
+
previous.worldId !== next.worldId ||
|
|
102
|
+
previous.entityKey !== next.entityKey ||
|
|
103
|
+
previous.layer !== next.layer ||
|
|
104
|
+
previous.sortKey !== next.sortKey ||
|
|
105
|
+
previous.visible !== next.visible
|
|
106
|
+
) {
|
|
107
|
+
return 'scene';
|
|
108
|
+
}
|
|
109
|
+
return 'none';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function sameArray(left: ArrayLike<number>, right: ArrayLike<number>): boolean {
|
|
113
|
+
if (left.length !== right.length) return false;
|
|
114
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
115
|
+
if (left[index] !== right[index]) return false;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function sameValue(
|
|
121
|
+
left: PointsLinesStyle | undefined,
|
|
122
|
+
right: PointsLinesStyle | undefined,
|
|
123
|
+
): boolean {
|
|
124
|
+
if (left?.kind !== right?.kind) return false;
|
|
125
|
+
if (left === undefined || right === undefined) return left === right;
|
|
126
|
+
if (left.kind === 'points' && right.kind === 'points') {
|
|
127
|
+
return left.sizePx === right.sizePx && left.shape === right.shape;
|
|
128
|
+
}
|
|
129
|
+
return left.kind === 'lines' && right.kind === 'lines' && left.widthPx === right.widthPx;
|
|
130
|
+
}
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
} from './helpers';
|
|
58
58
|
import { getOrCreateFromChain, MESH_SSBO_BYTES, MESH_UBO_FULL_ARRAY_BYTES } from './mesh-ssbo';
|
|
59
59
|
import type { PipelineState, RecordProfileRunner, RenderSystemInternals } from './render-context';
|
|
60
|
-
import { VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
60
|
+
import { POINTS_LINES_VIEW_BYTES, VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
61
61
|
|
|
62
62
|
function runHdrpClusterProfilePhase<T>(
|
|
63
63
|
runner: RecordProfileRunner | undefined,
|
|
@@ -161,6 +161,7 @@ export function buildPerFrameBindGroups(
|
|
|
161
161
|
b5View,
|
|
162
162
|
pipelineState.shadowParamsBuffer,
|
|
163
163
|
b8View,
|
|
164
|
+
pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
164
165
|
],
|
|
165
166
|
'view-main',
|
|
166
167
|
() => {
|
|
@@ -248,10 +249,21 @@ export function buildPerFrameBindGroups(
|
|
|
248
249
|
value: b8View,
|
|
249
250
|
},
|
|
250
251
|
},
|
|
252
|
+
{
|
|
253
|
+
binding: 10,
|
|
254
|
+
resource: {
|
|
255
|
+
kind: 'buffer',
|
|
256
|
+
value: {
|
|
257
|
+
buffer: pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
258
|
+
size: POINTS_LINES_VIEW_BYTES,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
251
262
|
// feat-20260625-spot-light-shadow-mapping w25: the per-spot
|
|
252
263
|
// fragment-read lightViewProj matrices fold into the View UBO
|
|
253
264
|
// (binding 0, `view.spotLightViewProj`) — no standalone binding 9
|
|
254
|
-
// (WebGL2 fragment uniform-buffer budget). binding
|
|
265
|
+
// (WebGL2 fragment uniform-buffer budget). binding 10 is the
|
|
266
|
+
// dedicated vertex-only Points/Lines viewport UBO.
|
|
255
267
|
],
|
|
256
268
|
});
|
|
257
269
|
if (!viewBindGroupResult.ok) throw viewBindGroupResult.error;
|
package/src/record/frame.ts
CHANGED
|
@@ -206,7 +206,7 @@ import {
|
|
|
206
206
|
executeCompiledFrameGraph,
|
|
207
207
|
type RenderFeatureGraphCandidate,
|
|
208
208
|
} from './typed-frame-graph';
|
|
209
|
-
import { writeViewUbo } from './view-ubo';
|
|
209
|
+
import { writePointsLinesViewUbo, writeViewUbo } from './view-ubo';
|
|
210
210
|
|
|
211
211
|
/**
|
|
212
212
|
* A frame-local material table. Repeated snapshot objects share one UBO slot;
|
|
@@ -320,6 +320,7 @@ export function recordFrame(
|
|
|
320
320
|
},
|
|
321
321
|
renderReadLeases?: readonly RenderReadLease[],
|
|
322
322
|
featureGraphCandidate?: RenderFeatureGraphCandidate,
|
|
323
|
+
pointsLinesOwner?: import('./render-context').PointsLinesRecordOwner,
|
|
323
324
|
): boolean {
|
|
324
325
|
// The `try / finally` wrapper advances `frameState.frameNumber` exactly
|
|
325
326
|
// once per `recordFrame` invocation regardless of which early-return
|
|
@@ -699,6 +700,15 @@ export function recordFrame(
|
|
|
699
700
|
lights,
|
|
700
701
|
frameState.spotShadowSnapshots,
|
|
701
702
|
);
|
|
703
|
+
if (pipelineState.pointsLinesViewBuffer !== undefined) {
|
|
704
|
+
writePointsLinesViewUbo(
|
|
705
|
+
internals.device.queue,
|
|
706
|
+
pipelineState.pointsLinesViewBuffer,
|
|
707
|
+
camera,
|
|
708
|
+
targetW,
|
|
709
|
+
targetH,
|
|
710
|
+
);
|
|
711
|
+
}
|
|
702
712
|
writeShadowCasterUniforms(
|
|
703
713
|
internals.device.queue,
|
|
704
714
|
pipelineState.shadowCasterCascadeBuffer,
|
|
@@ -816,6 +826,7 @@ export function recordFrame(
|
|
|
816
826
|
materialSlots,
|
|
817
827
|
materialSlotOwners,
|
|
818
828
|
materialSlotCount,
|
|
829
|
+
pointsLines: pointsLinesOwner,
|
|
819
830
|
materialBgAssemblyCache: frameState.materialBgAssemblyCache,
|
|
820
831
|
directionalShadowCacheReuse: directionalShadowCache.reuse,
|
|
821
832
|
...(profilePhase !== undefined ? { profilePhase } : {}),
|
|
@@ -21,6 +21,8 @@ import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
|
|
|
21
21
|
import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from '../materials';
|
|
22
22
|
import { SKIN_MATERIAL_SHADER_ID } from '../pbr-pipeline';
|
|
23
23
|
import { renderStateHash, variantSetFromVertexLayoutProjection } from '../pipeline-spec';
|
|
24
|
+
import type { PointsLinesRecordPlan } from '../points-lines/record';
|
|
25
|
+
import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
|
|
24
26
|
import type { RenderRecordPhase } from '../render-contract';
|
|
25
27
|
import type { MaterialSnapshot } from '../render-system-extract';
|
|
26
28
|
import { worldEntityKey } from './frame-snapshot';
|
|
@@ -40,6 +42,7 @@ import {
|
|
|
40
42
|
} from './mesh-ssbo';
|
|
41
43
|
import type { _InternalRenderPipelineContext } from './render-context';
|
|
42
44
|
import { MATERIAL_PER_ENTITY_STRIDE } from './render-context';
|
|
45
|
+
import { POINTS_LINES_VIEW_SLOT_STRIDE, writePointsLinesViewUbo } from './view-ubo';
|
|
43
46
|
|
|
44
47
|
type GeometryInstanceDraw = {
|
|
45
48
|
readonly instanceBuffer: Buffer;
|
|
@@ -115,6 +118,23 @@ function submitSubmeshDraws(
|
|
|
115
118
|
}
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Issues a prepared Points/Lines draw into the already-open geometry pass.
|
|
123
|
+
* Pass lifetime, attachments, bind groups, and submission remain owned by the
|
|
124
|
+
* existing main-pass interpreter.
|
|
125
|
+
*/
|
|
126
|
+
export function recordPointsLinesDraw(
|
|
127
|
+
pass: RhiRenderPassEncoder,
|
|
128
|
+
plan: PointsLinesRecordPlan,
|
|
129
|
+
): void {
|
|
130
|
+
if (plan.drawCount === 0) return;
|
|
131
|
+
if (plan.indexCount > 0) {
|
|
132
|
+
pass.drawIndexed(plan.indexCount, 1, 0, 0, 0);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
pass.draw(plan.vertexCount, 1, 0, 0);
|
|
136
|
+
}
|
|
137
|
+
|
|
118
138
|
/**
|
|
119
139
|
* feat-20260704 M3/w19: per-entity geometry (main colour) draw loop, extracted
|
|
120
140
|
* verbatim from recordMainPass. Walks `c.validatedOrdered`, selects the
|
|
@@ -155,6 +175,7 @@ export function recordGeometryDraws(
|
|
|
155
175
|
msaaActive,
|
|
156
176
|
validatedOrdered,
|
|
157
177
|
splitLdrSprite,
|
|
178
|
+
viewBindGroup,
|
|
158
179
|
} = c;
|
|
159
180
|
const colorFormatOverride = c.transparentColorFormat as GPUTextureFormat | undefined;
|
|
160
181
|
let lastVertexBuffer: GpuBuffer | null = null;
|
|
@@ -276,6 +297,13 @@ export function recordGeometryDraws(
|
|
|
276
297
|
const entry = validatedOrdered[i];
|
|
277
298
|
if (entry === undefined) continue;
|
|
278
299
|
|
|
300
|
+
// Points/Lines share the Standard geometry pass. Their retained source
|
|
301
|
+
// is prepared once by the renderer owner, while this loop remains the
|
|
302
|
+
// sole pass/draw submission owner and preserves the authored topology.
|
|
303
|
+
const pointsLinesSubmission = c.pointsLines?.prepare(entry, frameState.isHdrpActive);
|
|
304
|
+
if (entry.source.pointsLines !== undefined && pointsLinesSubmission?.plan.drawCount !== 1)
|
|
305
|
+
continue;
|
|
306
|
+
|
|
279
307
|
if (
|
|
280
308
|
passKind === 'forward' &&
|
|
281
309
|
c.gpuDrivenEntityKeys.has(worldEntityKey(entry.source.worldId, entry.source.entityKey))
|
|
@@ -316,6 +344,70 @@ export function recordGeometryDraws(
|
|
|
316
344
|
lastStencilReference = stencilReference;
|
|
317
345
|
}
|
|
318
346
|
|
|
347
|
+
if (pointsLinesSubmission !== undefined && entry.source.pointsLines !== undefined) {
|
|
348
|
+
const pointsLinesMaterial: MaterialSnapshot = {
|
|
349
|
+
...entry.source.material,
|
|
350
|
+
materialShaderId: POINTS_LINES_MATERIAL_SHADER_ID,
|
|
351
|
+
materialParamSchema:
|
|
352
|
+
runtime.getParamSchema?.(POINTS_LINES_MATERIAL_SHADER_ID) ??
|
|
353
|
+
entry.source.material.materialParamSchema,
|
|
354
|
+
};
|
|
355
|
+
const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
|
|
356
|
+
const pointsLinesBindGroup = resolveMaterialBindGroup(
|
|
357
|
+
materialSlot,
|
|
358
|
+
pointsLinesMaterial,
|
|
359
|
+
entry.source.entityKey,
|
|
360
|
+
entry.world ?? c.world,
|
|
361
|
+
);
|
|
362
|
+
const pointsLinesPipeline = resolveMaterialPipeline(
|
|
363
|
+
POINTS_LINES_MATERIAL_SHADER_ID,
|
|
364
|
+
{
|
|
365
|
+
...pointsLinesMaterial.renderState,
|
|
366
|
+
cullMode: 'none',
|
|
367
|
+
},
|
|
368
|
+
'triangle-list',
|
|
369
|
+
'uint32',
|
|
370
|
+
undefined,
|
|
371
|
+
colorFormatOverride,
|
|
372
|
+
pointsLinesSubmission.layoutProjection,
|
|
373
|
+
);
|
|
374
|
+
if (pointsLinesPipeline === null) continue;
|
|
375
|
+
if (pipelineState.pointsLinesViewBuffer === undefined) continue;
|
|
376
|
+
writePointsLinesViewUbo(
|
|
377
|
+
runtime.device.queue,
|
|
378
|
+
pipelineState.pointsLinesViewBuffer,
|
|
379
|
+
c.camera,
|
|
380
|
+
c.targetW,
|
|
381
|
+
c.targetH,
|
|
382
|
+
entry.source.transform.world,
|
|
383
|
+
entry.source.pointsLines.style,
|
|
384
|
+
i * POINTS_LINES_VIEW_SLOT_STRIDE,
|
|
385
|
+
);
|
|
386
|
+
pass.setBindGroup(
|
|
387
|
+
0,
|
|
388
|
+
viewBindGroup as BindGroup,
|
|
389
|
+
new Uint32Array([i * POINTS_LINES_VIEW_SLOT_STRIDE]),
|
|
390
|
+
0,
|
|
391
|
+
1,
|
|
392
|
+
);
|
|
393
|
+
pass.setPipeline(pointsLinesPipeline);
|
|
394
|
+
pass.setVertexBuffer(0, pointsLinesSubmission.vertexBuffer);
|
|
395
|
+
pass.setIndexBuffer(pointsLinesSubmission.indexBuffer, 'uint32');
|
|
396
|
+
materialGroup1DynamicOffsets[0] = materialSlot * MATERIAL_PER_ENTITY_STRIDE;
|
|
397
|
+
pass.setBindGroup(1, pointsLinesBindGroup, materialGroup1DynamicOffsets, 0, 1);
|
|
398
|
+
const pointsLinesMeshGroup = meshBindGroup ?? meshGroup2;
|
|
399
|
+
if (pointsLinesMeshGroup === null) continue;
|
|
400
|
+
pass.setBindGroup(2, pointsLinesMeshGroup, meshGroup2DynamicOffsets);
|
|
401
|
+
pass.setBindGroup(
|
|
402
|
+
3,
|
|
403
|
+
identityInstanceDraws[0]?.instanceBindGroup ??
|
|
404
|
+
resolveGeometryInstancesBindGroup(c, identityInstanceBuffer),
|
|
405
|
+
);
|
|
406
|
+
recordPointsLinesDraw(pass, pointsLinesSubmission.plan);
|
|
407
|
+
pass.setBindGroup(0, viewBindGroup as BindGroup, [0]);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
|
|
319
411
|
if (entry.mesh.vertexBuffer !== lastVertexBuffer) {
|
|
320
412
|
pass.setVertexBuffer(0, entry.mesh.vertexBuffer.handle);
|
|
321
413
|
lastVertexBuffer = entry.mesh.vertexBuffer;
|
|
@@ -203,7 +203,7 @@ export function recordSpritePass(
|
|
|
203
203
|
) as never,
|
|
204
204
|
);
|
|
205
205
|
|
|
206
|
-
spritePass.setBindGroup(0, viewBindGroup as BindGroup);
|
|
206
|
+
spritePass.setBindGroup(0, viewBindGroup as BindGroup, [0]);
|
|
207
207
|
|
|
208
208
|
// feat-20260625-refactor-sprite-as-transparent-mesh M3 / w14 (D-7):
|
|
209
209
|
// sprite PSO resolution migrated from the dedicated boot-time PSO
|
package/src/record/main-pass.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { Handle, PassKind, PassSelector } from '@forgeax/engine-types';
|
|
|
10
10
|
import { createHdrpUnifiedBindGroup, getOrCreateHdrpBuffers } from '../hdrp-buffers';
|
|
11
11
|
import { getOrCreateIblCache } from '../ibl/IblPipelineCache';
|
|
12
12
|
import { buildBeginRenderPassDescriptor } from '../pipeline-spec';
|
|
13
|
+
import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
|
|
13
14
|
import type { RenderRecordPhase } from '../render-contract';
|
|
14
15
|
import type { MaterialSnapshot } from '../render-system-extract';
|
|
15
16
|
import { recordGeometryDraws } from './main-pass-geometry';
|
|
@@ -535,10 +536,12 @@ export function recordMainPass(
|
|
|
535
536
|
entityKey: number,
|
|
536
537
|
materialWorld: World = world,
|
|
537
538
|
): BindGroup =>
|
|
538
|
-
|
|
539
|
-
|
|
539
|
+
material.materialShaderId !== POINTS_LINES_MATERIAL_SHADER_ID
|
|
540
|
+
? (preparedMaterialBindGroups[materialSlot] ??
|
|
541
|
+
buildPerSubmeshMaterialBg(material, entityKey, materialWorld))
|
|
542
|
+
: buildPerSubmeshMaterialBg(material, entityKey, materialWorld);
|
|
540
543
|
|
|
541
|
-
pass.setBindGroup(0, viewBindGroup as BindGroup);
|
|
544
|
+
pass.setBindGroup(0, viewBindGroup as BindGroup, [0]);
|
|
542
545
|
|
|
543
546
|
// Track which (mesh-vertex-buffer, mesh-index-buffer, pipeline) combo
|
|
544
547
|
// was last bound so consecutive entities sharing the same combo skip
|