@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
|
@@ -40,6 +40,16 @@ import type { SkylightFallback } from '../ibl/skylight-bind-group';
|
|
|
40
40
|
import type { HealthListenerRegistry, RhiErrorListenerRegistry } from '../lifecycle';
|
|
41
41
|
import type { StandardProfile } from '../pipeline/standard-profile';
|
|
42
42
|
import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
|
|
43
|
+
import type { PointsLinesInspection } from '../points-lines/inspection';
|
|
44
|
+
import type { PointsLinesRecordPlan } from '../points-lines/record';
|
|
45
|
+
|
|
46
|
+
export interface PointsLinesRecordSubmission {
|
|
47
|
+
readonly plan: PointsLinesRecordPlan;
|
|
48
|
+
readonly vertexBuffer: Buffer;
|
|
49
|
+
readonly indexBuffer: Buffer;
|
|
50
|
+
readonly layoutProjection: VertexLayoutProjection;
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
import type {
|
|
44
54
|
RenderDebugOverlay,
|
|
45
55
|
RenderPipelineContext,
|
|
@@ -103,6 +113,14 @@ export interface RenderSystemRuntime {
|
|
|
103
113
|
readonly dynamicTextureStore?: DynamicTextureStore | undefined;
|
|
104
114
|
}
|
|
105
115
|
|
|
116
|
+
/** Renderer-owned bridge from retained Points/Lines facts into main geometry. */
|
|
117
|
+
export interface PointsLinesRecordOwner {
|
|
118
|
+
prepare(entry: ValidatedRenderable, clustered: boolean): PointsLinesRecordSubmission | undefined;
|
|
119
|
+
beginFrame(): void;
|
|
120
|
+
resetForDeviceLoss(): void;
|
|
121
|
+
inspections(): readonly PointsLinesInspection[];
|
|
122
|
+
}
|
|
123
|
+
|
|
106
124
|
export interface PerPassResources {
|
|
107
125
|
depthTexture: Texture | null;
|
|
108
126
|
depthTextureView: TextureView | null;
|
|
@@ -184,6 +202,7 @@ export interface PipelineState {
|
|
|
184
202
|
readonly materialBindGroupLayout: BindGroupLayout;
|
|
185
203
|
readonly meshBindGroupLayout: BindGroupLayout;
|
|
186
204
|
readonly viewUniformBuffer: Buffer;
|
|
205
|
+
readonly pointsLinesViewBuffer?: Buffer;
|
|
187
206
|
readonly shadowCasterCascadeBuffer: Buffer;
|
|
188
207
|
readonly materialUniformBuffer: { readonly buffer: Buffer; readonly sizeInBytes: number };
|
|
189
208
|
readonly meshStorageBuffer: { readonly buffer: Buffer; readonly sizeInBytes: number };
|
|
@@ -281,6 +300,7 @@ export interface _StandardForwardSceneView {
|
|
|
281
300
|
readonly materialSlots: readonly MaterialSnapshot[];
|
|
282
301
|
readonly materialSlotOwners: readonly number[];
|
|
283
302
|
readonly materialSlotCount: number;
|
|
303
|
+
readonly pointsLines: PointsLinesRecordOwner | undefined;
|
|
284
304
|
readonly materialBgAssemblyCache: Map<string, MaterialBgAssemblyCacheEntry>;
|
|
285
305
|
materialUboPayloadCache?: {
|
|
286
306
|
readonly materialSlots: readonly MaterialSnapshot[];
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from './mesh-ssbo';
|
|
30
30
|
import type { _InternalRenderPipelineContext } from './render-context';
|
|
31
31
|
import { STANDARD_PBR_UBO_SIZE } from './render-context';
|
|
32
|
-
import { pointShadowViewOffset, VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
32
|
+
import { POINTS_LINES_VIEW_BYTES, pointShadowViewOffset, VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
33
33
|
|
|
34
34
|
export const SHADOW_CASTER_SLOT_STRIDE = 256;
|
|
35
35
|
export const SHADOW_CASTER_BUFFER_SIZE = SHADOW_CASTER_SLOT_STRIDE * 8;
|
|
@@ -88,6 +88,7 @@ function ensureTypedShadowViewBg(
|
|
|
88
88
|
pipelineState.shadowParamsBuffer,
|
|
89
89
|
pipelineState.shadowCasterCascadeBuffer,
|
|
90
90
|
pipelineState.shadowFallbackTextureView,
|
|
91
|
+
pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
91
92
|
],
|
|
92
93
|
variant,
|
|
93
94
|
() => {
|
|
@@ -140,7 +141,7 @@ function ensureTypedShadowViewBg(
|
|
|
140
141
|
value: {
|
|
141
142
|
buffer: pipelineState.shadowCasterCascadeBuffer,
|
|
142
143
|
offset: cascadeOffset,
|
|
143
|
-
size:
|
|
144
|
+
size: POINTS_LINES_VIEW_BYTES,
|
|
144
145
|
},
|
|
145
146
|
},
|
|
146
147
|
},
|
|
@@ -148,6 +149,16 @@ function ensureTypedShadowViewBg(
|
|
|
148
149
|
binding: 8,
|
|
149
150
|
resource: { kind: 'textureView', value: pipelineState.shadowFallbackTextureView },
|
|
150
151
|
},
|
|
152
|
+
{
|
|
153
|
+
binding: 10,
|
|
154
|
+
resource: {
|
|
155
|
+
kind: 'buffer',
|
|
156
|
+
value: {
|
|
157
|
+
buffer: pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
158
|
+
size: 80,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
151
162
|
],
|
|
152
163
|
});
|
|
153
164
|
if (!created.ok) throw created.error;
|
|
@@ -207,7 +218,7 @@ export function encodeDirectionalShadowPass(
|
|
|
207
218
|
c.frameState.directionalShadowCacheRecorded = true;
|
|
208
219
|
pass.setViewport(viewport.x, viewport.y, viewport.w, viewport.h, 0, 1);
|
|
209
220
|
pass.setPipeline(pipeline);
|
|
210
|
-
pass.setBindGroup(0, viewBg);
|
|
221
|
+
pass.setBindGroup(0, viewBg, [0]);
|
|
211
222
|
pass.setBindGroup(1, materialBg, [0]);
|
|
212
223
|
recordShadowCasterDraws(
|
|
213
224
|
c,
|
|
@@ -237,7 +248,7 @@ export function encodePointShadowPass(
|
|
|
237
248
|
const materialBg = ensureSpotShadowMaterialBg(c);
|
|
238
249
|
if (pipeline === null || viewBg === null || materialBg === null) return;
|
|
239
250
|
pass.setPipeline(pipeline);
|
|
240
|
-
pass.setBindGroup(0, viewBg);
|
|
251
|
+
pass.setBindGroup(0, viewBg, [0]);
|
|
241
252
|
pass.setBindGroup(1, materialBg, [0]);
|
|
242
253
|
recordShadowCasterDraws(
|
|
243
254
|
c,
|
|
@@ -285,7 +296,7 @@ export function encodeSpotShadowPass(
|
|
|
285
296
|
1,
|
|
286
297
|
);
|
|
287
298
|
pass.setPipeline(pipeline);
|
|
288
|
-
pass.setBindGroup(0, viewBg);
|
|
299
|
+
pass.setBindGroup(0, viewBg, [0]);
|
|
289
300
|
pass.setBindGroup(1, materialBg, [0]);
|
|
290
301
|
// Reuse the shared caster recorder. The compact spot-only loop used to
|
|
291
302
|
// depend on instance bindings created by a directional pass, so a scene
|
|
@@ -4,6 +4,7 @@ import { toShared } from '@forgeax/engine-types';
|
|
|
4
4
|
import { buildBeginRenderPassDescriptor } from '../pipeline-spec';
|
|
5
5
|
import { getOrCreateFromChain } from './mesh-ssbo';
|
|
6
6
|
import type { _InternalRenderPipelineContext } from './render-context';
|
|
7
|
+
import { VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* feat-20260531-skybox-env-background M2 / w8: skybox pass recording stub.
|
|
@@ -111,7 +112,7 @@ export function recordSkyboxPass(
|
|
|
111
112
|
binding: 2,
|
|
112
113
|
resource: {
|
|
113
114
|
kind: 'buffer',
|
|
114
|
-
value: { buffer: pipelineState.viewUniformBuffer },
|
|
115
|
+
value: { buffer: pipelineState.viewUniformBuffer, size: VIEW_UNIFORM_BYTES },
|
|
115
116
|
},
|
|
116
117
|
},
|
|
117
118
|
{
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
RenderGraphBuilder,
|
|
5
5
|
} from '@forgeax/engine-render-graph';
|
|
6
6
|
import type {
|
|
7
|
-
BindGroup,
|
|
8
7
|
BindGroupLayout,
|
|
9
8
|
CommandBuffer,
|
|
10
9
|
RenderPipeline,
|
|
@@ -20,7 +19,10 @@ import {
|
|
|
20
19
|
} from '../features/host';
|
|
21
20
|
import { type RenderFeaturePlannedFrame, renderFeaturePlanSignature } from '../features/plan';
|
|
22
21
|
import { projectRenderFeaturePlans as projectPlanExecutions } from '../features/render-graph-contribution';
|
|
23
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
RenderFeatureGraphBindingsResolution,
|
|
24
|
+
RenderFeatureGraphTargetResolver,
|
|
25
|
+
} from '../features/render-graph-raster';
|
|
24
26
|
import { isRenderFeatureTargetHandle } from '../features/targets';
|
|
25
27
|
import type { PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
|
|
26
28
|
import { STANDARD_PIPELINE_ID } from '../pipeline/standard-profile';
|
|
@@ -251,7 +253,7 @@ function resolveTargetBindings(input: {
|
|
|
251
253
|
};
|
|
252
254
|
readonly resources: GraphResourceResolver;
|
|
253
255
|
readonly resolveTarget: RenderFeatureGraphTargetResolver;
|
|
254
|
-
}):
|
|
256
|
+
}): RenderFeatureGraphBindingsResolution | undefined {
|
|
255
257
|
const frame = input.frame as import('./render-context')._InternalRenderPipelineContext;
|
|
256
258
|
const descriptor = input.binding.descriptor;
|
|
257
259
|
const pipeline = input.binding.pipeline as
|
|
@@ -294,19 +296,18 @@ function resolveTargetBindings(input: {
|
|
|
294
296
|
return created.value;
|
|
295
297
|
}
|
|
296
298
|
if (sceneDepth === undefined) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
);
|
|
299
|
+
const viewBindGroup = buildPerFrameBindGroups(
|
|
300
|
+
frame.runtime as RenderSystemInternals,
|
|
301
|
+
frame.frameState,
|
|
302
|
+
frame.pipelineState,
|
|
303
|
+
frame.validated.length > 0,
|
|
304
|
+
frame.bindGroupCounts,
|
|
305
|
+
{
|
|
306
|
+
directionalShadow: frame.frameState.currentDirectionalShadowView ?? undefined,
|
|
307
|
+
spotShadow: frame.frameState.currentSpotShadowView ?? undefined,
|
|
308
|
+
},
|
|
309
|
+
).viewBindGroup;
|
|
310
|
+
return viewBindGroup === null ? undefined : { handle: viewBindGroup, dynamicOffsets: [0] };
|
|
310
311
|
}
|
|
311
312
|
const target = input.resolveTarget(sceneDepth);
|
|
312
313
|
if (target === undefined) return undefined;
|
package/src/record/view-ubo.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { mat4 } from '@forgeax/engine-math';
|
|
10
10
|
import type { Buffer, RhiQueue } from '@forgeax/engine-rhi';
|
|
11
|
+
import type { PointsLinesStyle } from '../points-lines/snapshot';
|
|
11
12
|
import type { CameraSnapshot } from '../render-contract';
|
|
12
13
|
import type {
|
|
13
14
|
DirectionalLightSnapshot,
|
|
@@ -18,6 +19,11 @@ import { clampPcfKernelSize } from './frame-snapshot';
|
|
|
18
19
|
import { computeProjectionMatrix, computeViewMatrix } from './helpers';
|
|
19
20
|
|
|
20
21
|
export const VIEW_UNIFORM_BYTES = 960;
|
|
22
|
+
export const POINTS_LINES_VIEW_BYTES = 160;
|
|
23
|
+
export const POINTS_LINES_VIEW_SLOT_STRIDE = 256;
|
|
24
|
+
export const POINTS_LINES_VIEW_SLOT_COUNT = 1024;
|
|
25
|
+
export const POINTS_LINES_VIEW_BUFFER_SIZE =
|
|
26
|
+
POINTS_LINES_VIEW_SLOT_STRIDE * POINTS_LINES_VIEW_SLOT_COUNT;
|
|
21
27
|
export const VIEW_UNIFORM_SLOT_STRIDE = 1024;
|
|
22
28
|
export const POINT_SHADOW_VIEW_SLOT_COUNT = 24;
|
|
23
29
|
export const VIEW_UNIFORM_BUFFER_SIZE =
|
|
@@ -27,6 +33,47 @@ export function pointShadowViewOffset(layer: number, face: number): number {
|
|
|
27
33
|
return VIEW_UNIFORM_SLOT_STRIDE * (1 + layer * 6 + face);
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
export function writePointsLinesViewUbo(
|
|
37
|
+
queue: RhiQueue,
|
|
38
|
+
buffer: Buffer,
|
|
39
|
+
camera: CameraSnapshot,
|
|
40
|
+
width: number,
|
|
41
|
+
height: number,
|
|
42
|
+
model?: ArrayLike<number>,
|
|
43
|
+
style?: PointsLinesStyle,
|
|
44
|
+
byteOffset = 0,
|
|
45
|
+
): void {
|
|
46
|
+
const projection = computeProjectionMatrix(camera);
|
|
47
|
+
const view = computeViewMatrix(camera);
|
|
48
|
+
const worldViewProj = mat4.create();
|
|
49
|
+
mat4.multiply(worldViewProj, projection, view);
|
|
50
|
+
const payload = new Float32Array(40);
|
|
51
|
+
payload.set(worldViewProj);
|
|
52
|
+
if (model === undefined) {
|
|
53
|
+
payload[16] = 1;
|
|
54
|
+
payload[21] = 1;
|
|
55
|
+
payload[26] = 1;
|
|
56
|
+
payload[31] = 1;
|
|
57
|
+
} else {
|
|
58
|
+
for (let index = 0; index < 16; index += 1) {
|
|
59
|
+
payload[16 + index] = model[index] ?? 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
payload[32] = width;
|
|
63
|
+
payload[33] = height;
|
|
64
|
+
if (style?.kind === 'points') {
|
|
65
|
+
payload[36] = style.sizePx;
|
|
66
|
+
payload[38] = style.shape === 'circle' ? 1 : 0;
|
|
67
|
+
} else if (style?.kind === 'lines') {
|
|
68
|
+
payload[36] = style.widthPx;
|
|
69
|
+
payload[37] = 1;
|
|
70
|
+
} else {
|
|
71
|
+
payload[36] = 1;
|
|
72
|
+
}
|
|
73
|
+
const uploaded = queue.writeBuffer(buffer, byteOffset, payload);
|
|
74
|
+
if (!uploaded.ok) throw uploaded.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
30
77
|
/**
|
|
31
78
|
* feat-20260704 M3/w18: assemble + upload the per-frame View UBO payload.
|
|
32
79
|
*
|
package/src/render-contract.ts
CHANGED
|
@@ -24,9 +24,12 @@ import type { ImageError } from '@forgeax/engine-types';
|
|
|
24
24
|
import type { RhiBackendInstrumentation } from './assembly/backend-contract';
|
|
25
25
|
import type { Antialias, BloomEnabled, Tonemap } from './components/camera';
|
|
26
26
|
import type { RenderError } from './errors/render';
|
|
27
|
-
import type { RenderFeature } from './features/types';
|
|
27
|
+
import type { RenderFeature, RenderFeatureDiagnostics } from './features/types';
|
|
28
28
|
import type { PostProcessShaderEntry } from './fullscreen-post-process-pass';
|
|
29
29
|
import type { RenderSceneInspection } from './inspection-types';
|
|
30
|
+
|
|
31
|
+
export type { PointsLinesInspection } from './points-lines/inspection';
|
|
32
|
+
|
|
30
33
|
import type { MeshMaterialBindingObservation } from './mesh-material-bindings';
|
|
31
34
|
import type { StandardProfile } from './pipeline/standard-profile';
|
|
32
35
|
import type { PostProcessError } from './post-process-errors';
|
|
@@ -172,7 +175,13 @@ export type RendererEvent =
|
|
|
172
175
|
readonly previous: RendererState;
|
|
173
176
|
readonly current: RendererState;
|
|
174
177
|
}
|
|
175
|
-
| { readonly kind: 'error'; readonly error: RenderError }
|
|
178
|
+
| { readonly kind: 'error'; readonly error: RenderError }
|
|
179
|
+
/** Emitted only after the renderer's sole submit path returns a receipt. */
|
|
180
|
+
| {
|
|
181
|
+
readonly kind: 'frame-submitted';
|
|
182
|
+
readonly frameId: number;
|
|
183
|
+
readonly deviceGeneration: number;
|
|
184
|
+
};
|
|
176
185
|
|
|
177
186
|
/** Package-local listener alias used by the renderer assembly implementation. */
|
|
178
187
|
export type RendererEventListener = (event: RendererEvent) => void;
|
|
@@ -252,6 +261,8 @@ export interface RenderInspection {
|
|
|
252
261
|
readonly deviceGeneration: number;
|
|
253
262
|
};
|
|
254
263
|
readonly features: readonly string[];
|
|
264
|
+
/** Truthful lifecycle facts for every registered declarative feature. */
|
|
265
|
+
readonly featureDiagnostics: readonly RenderFeatureDiagnostics[];
|
|
255
266
|
readonly frustumStats: { readonly culled: number; readonly total: number };
|
|
256
267
|
readonly visibilityStats: { readonly explicitlyHidden: number };
|
|
257
268
|
readonly renderScene: RenderSceneInspection;
|
|
@@ -159,13 +159,17 @@ import {
|
|
|
159
159
|
DirectionalLight,
|
|
160
160
|
Instances,
|
|
161
161
|
Layer,
|
|
162
|
+
Lines,
|
|
162
163
|
MeshFilter,
|
|
163
164
|
MeshRenderer,
|
|
164
165
|
PointLight,
|
|
165
166
|
PointLightShadow,
|
|
167
|
+
Points,
|
|
166
168
|
PostProcessParams,
|
|
169
|
+
pointShapeFromU32,
|
|
167
170
|
SkyboxBackground,
|
|
168
171
|
Skylight,
|
|
172
|
+
SortKey,
|
|
169
173
|
SpotLight,
|
|
170
174
|
SpriteInstances,
|
|
171
175
|
SpriteRegionOverride,
|
|
@@ -191,6 +195,8 @@ import {
|
|
|
191
195
|
resolveMeshMaterialBindings,
|
|
192
196
|
} from './mesh-material-bindings';
|
|
193
197
|
import { isStandardPbrMaterialShader } from './pbr-pipeline';
|
|
198
|
+
import { expandPointsLinesBounds } from './points-lines/bounds';
|
|
199
|
+
import type { PointsLinesRetainedSnapshot, PointsLinesStyle } from './points-lines/snapshot';
|
|
194
200
|
import { type CameraSnapshot, STANDARD_TONEMAP_FEATURE_ID } from './render-contract';
|
|
195
201
|
import { getActiveCamera, selectActiveCameraIndex } from './systems/active-camera';
|
|
196
202
|
import { selectPasses } from './systems/pass-selector';
|
|
@@ -598,6 +604,8 @@ export interface RenderableSnapshot {
|
|
|
598
604
|
* deltas and the record stage only receives this frozen POD snapshot.
|
|
599
605
|
*/
|
|
600
606
|
readonly morph?: MorphSnapshot;
|
|
607
|
+
/** Detached M2 authoring facts for the optional Points/Lines projection. */
|
|
608
|
+
readonly pointsLines?: PointsLinesRetainedSnapshot;
|
|
601
609
|
}
|
|
602
610
|
|
|
603
611
|
export interface MorphSnapshot {
|
|
@@ -870,7 +878,7 @@ function gpuDrivenMaterialResourceClass(material: MaterialSnapshot): string {
|
|
|
870
878
|
//
|
|
871
879
|
// Plan-strategy D-3: single dispatch list sorted by queue value,
|
|
872
880
|
// replacing the old three-bucket opaque/transparent/overlay dispatch.
|
|
873
|
-
// Each entry carries the per-pass render-state
|
|
881
|
+
// Each entry carries the per-pass render-state and entry-point
|
|
874
882
|
// data from the resolved MaterialPass so the record stage
|
|
875
883
|
// reads them without re-resolving the material.
|
|
876
884
|
|
|
@@ -948,7 +956,9 @@ function appendMaterialDispatchEntries(
|
|
|
948
956
|
layer,
|
|
949
957
|
tags: passState.tags ?? {},
|
|
950
958
|
renderState: pipelineRenderState(passState),
|
|
951
|
-
|
|
959
|
+
// Material module identity is already closed in the cooked program.
|
|
960
|
+
// It must never become a draw-time define map.
|
|
961
|
+
defines: undefined,
|
|
952
962
|
vertexEntry: pass.program.vertexEntry,
|
|
953
963
|
fragmentEntry: pass.program.fragmentEntry,
|
|
954
964
|
materialShaderId: runtimeMaterialShaderId(pass.program.module, pass.name),
|
|
@@ -1405,9 +1415,8 @@ function materialParametersToParamSchema(
|
|
|
1405
1415
|
? ENGINE_INJECTED_TEXTURE_FIELDS
|
|
1406
1416
|
: undefined;
|
|
1407
1417
|
return parameters.flatMap((parameter): ParamSchemaEntry[] => {
|
|
1408
|
-
//
|
|
1409
|
-
|
|
1410
|
-
if (parameter.static || parameter.type === 'bool') return [];
|
|
1418
|
+
// Boolean material values are runtime-only and have no UBO representation.
|
|
1419
|
+
if (parameter.type === 'bool') return [];
|
|
1411
1420
|
if (parameter.type === 'texture') {
|
|
1412
1421
|
if (engineInjectedTextureFields?.has(parameter.name) === true) return [];
|
|
1413
1422
|
return [{ name: parameter.name, type: 'texture2d' }];
|
|
@@ -2387,7 +2396,11 @@ export function extractFrames(
|
|
|
2387
2396
|
}
|
|
2388
2397
|
hiddenEntityReports.push(...f.hiddenEntityReports);
|
|
2389
2398
|
for (const r of f.renderables) {
|
|
2390
|
-
renderables.push({
|
|
2399
|
+
renderables.push({
|
|
2400
|
+
...r,
|
|
2401
|
+
worldId: wId,
|
|
2402
|
+
...(r.pointsLines === undefined ? {} : { pointsLines: { ...r.pointsLines, worldId: wId } }),
|
|
2403
|
+
});
|
|
2391
2404
|
}
|
|
2392
2405
|
|
|
2393
2406
|
// D-3: dispatch — per-world renderableIndex rebased by base offset.
|
|
@@ -3236,6 +3249,9 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3236
3249
|
MorphWeights,
|
|
3237
3250
|
SpriteRegionOverride,
|
|
3238
3251
|
SpriteInstances,
|
|
3252
|
+
Points,
|
|
3253
|
+
Lines,
|
|
3254
|
+
SortKey,
|
|
3239
3255
|
],
|
|
3240
3256
|
})
|
|
3241
3257
|
.unwrap();
|
|
@@ -3288,6 +3304,11 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
3288
3304
|
// - 'sprite-instances-count-mismatch'
|
|
3289
3305
|
// (transforms.length / 16 !== regions.length / 4) — stride pair desync.
|
|
3290
3306
|
const hasSpriteInstances = row.has(SpriteInstances);
|
|
3307
|
+
const points = row.get(Points);
|
|
3308
|
+
const lines = row.get(Lines);
|
|
3309
|
+
const sortKey = row.get(SortKey)?.value;
|
|
3310
|
+
const pointsLinesComponent =
|
|
3311
|
+
points !== undefined ? 'Points' : lines !== undefined ? 'Lines' : undefined;
|
|
3291
3312
|
const isRenderable = hasTransform && hasMeshFilter;
|
|
3292
3313
|
|
|
3293
3314
|
// feat-20260601 D-3: the resolved world transform is read per-entity from
|
|
@@ -4342,6 +4363,38 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4342
4363
|
}
|
|
4343
4364
|
}
|
|
4344
4365
|
|
|
4366
|
+
const pointsLinesStyle: PointsLinesStyle | undefined =
|
|
4367
|
+
points !== undefined
|
|
4368
|
+
? (() => {
|
|
4369
|
+
const shape = pointShapeFromU32(points.shape);
|
|
4370
|
+
return shape === undefined
|
|
4371
|
+
? undefined
|
|
4372
|
+
: { kind: 'points' as const, sizePx: points.sizePx, shape };
|
|
4373
|
+
})()
|
|
4374
|
+
: lines === undefined
|
|
4375
|
+
? undefined
|
|
4376
|
+
: { kind: 'lines' as const, widthPx: lines.widthPx };
|
|
4377
|
+
const cullingLocalAabb = expandPointsLinesBounds(localAabb ?? [], pointsLinesStyle);
|
|
4378
|
+
const pointsLines =
|
|
4379
|
+
pointsLinesComponent === undefined
|
|
4380
|
+
? undefined
|
|
4381
|
+
: ({
|
|
4382
|
+
worldId: 0,
|
|
4383
|
+
entityKey: entity,
|
|
4384
|
+
component: pointsLinesComponent,
|
|
4385
|
+
meshHandle: assetHandleRaw,
|
|
4386
|
+
meshGeneration: assets?.catalogEpoch ?? 0,
|
|
4387
|
+
materialHandle: handleRaw,
|
|
4388
|
+
materialGeneration: assets?.catalogEpoch ?? 0,
|
|
4389
|
+
style: pointsLinesStyle,
|
|
4390
|
+
layer: layerVal,
|
|
4391
|
+
sortKey,
|
|
4392
|
+
visible: true,
|
|
4393
|
+
sourceBounds: cullingLocalAabb,
|
|
4394
|
+
viewport: { width: 0, height: 0, dpr: 1 },
|
|
4395
|
+
projection: identityProjection(),
|
|
4396
|
+
} satisfies PointsLinesRetainedSnapshot);
|
|
4397
|
+
|
|
4345
4398
|
let nonIndexedFirst = 0;
|
|
4346
4399
|
const gpuDrivenDraws = gpuDrivenSubmeshes.flatMap((submesh) => {
|
|
4347
4400
|
const drawMaterial = materialsArr[submesh.materialSlot] ?? materialSnap;
|
|
@@ -4364,7 +4417,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4364
4417
|
const baseRenderable: RenderableSnapshot = {
|
|
4365
4418
|
assetHandle: Math.round(fAssetHandle ?? 0),
|
|
4366
4419
|
transform: transformSnap,
|
|
4367
|
-
...(localAabb !== undefined ? { localAabb } : {}),
|
|
4420
|
+
...(localAabb !== undefined ? { localAabb: cullingLocalAabb } : {}),
|
|
4368
4421
|
material: materialSnap,
|
|
4369
4422
|
materials: materialsArr,
|
|
4370
4423
|
materialBindingSources,
|
|
@@ -4375,6 +4428,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4375
4428
|
...(skinSlice !== undefined ? { skin: skinSlice } : {}),
|
|
4376
4429
|
...(morph !== undefined ? { morph } : {}),
|
|
4377
4430
|
...(spriteInstancesSnap !== undefined ? { spriteInstances: spriteInstancesSnap } : {}),
|
|
4431
|
+
...(pointsLines !== undefined ? { pointsLines } : {}),
|
|
4378
4432
|
};
|
|
4379
4433
|
|
|
4380
4434
|
// feat-20260528-frustum-culling M3 / w10: frustum culling check.
|
|
@@ -4388,7 +4442,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
4388
4442
|
// directly (no compose) -- same source the record stage feeds
|
|
4389
4443
|
// the mesh SSBO, so cull stays same-source with render (AC-05).
|
|
4390
4444
|
const worldAabb = box3.create();
|
|
4391
|
-
box3.transformBox3(worldAabb,
|
|
4445
|
+
box3.transformBox3(worldAabb, cullingLocalAabb, transformSnap.world);
|
|
4392
4446
|
|
|
4393
4447
|
// Test against all cameras. Entity is visible if one camera
|
|
4394
4448
|
// frustum intersects the world-space AABB (or planes are empty
|
|
@@ -4520,3 +4574,12 @@ export function defaultMaterialSnapshot(materialHandle = 0): MaterialSnapshot {
|
|
|
4520
4574
|
materialHandle,
|
|
4521
4575
|
};
|
|
4522
4576
|
}
|
|
4577
|
+
|
|
4578
|
+
function identityProjection(): Float32Array {
|
|
4579
|
+
const projection = new Float32Array(16);
|
|
4580
|
+
projection[0] = 1;
|
|
4581
|
+
projection[5] = 1;
|
|
4582
|
+
projection[10] = 1;
|
|
4583
|
+
projection[15] = 1;
|
|
4584
|
+
return projection;
|
|
4585
|
+
}
|