@aura3d/rendering 4.3.7 → 4.6.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/dist/DepthPass.d.ts +7 -0
- package/dist/DepthPass.d.ts.map +1 -1
- package/dist/DepthPass.js +43 -6
- package/dist/DepthPass.js.map +1 -1
- package/dist/ForwardPass.d.ts +82 -5
- package/dist/ForwardPass.d.ts.map +1 -1
- package/dist/ForwardPass.js +180 -35
- package/dist/ForwardPass.js.map +1 -1
- package/dist/MaterialBinding.js +6 -2
- package/dist/MaterialBinding.js.map +1 -1
- package/dist/RenderDevice.d.ts +5 -0
- package/dist/RenderDevice.d.ts.map +1 -1
- package/dist/RenderDevice.js.map +1 -1
- package/dist/Renderer.d.ts +8 -0
- package/dist/Renderer.d.ts.map +1 -1
- package/dist/Renderer.js +37 -6
- package/dist/Renderer.js.map +1 -1
- package/dist/RendererPostprocessPlan.d.ts.map +1 -1
- package/dist/RendererPostprocessPlan.js +8 -1
- package/dist/RendererPostprocessPlan.js.map +1 -1
- package/dist/ShaderChunks.d.ts.map +1 -1
- package/dist/ShaderChunks.js +3 -1
- package/dist/ShaderChunks.js.map +1 -1
- package/dist/ShaderLibrary.d.ts.map +1 -1
- package/dist/ShaderLibrary.js +17 -5
- package/dist/ShaderLibrary.js.map +1 -1
- package/dist/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/ShaderLibraryCore.js +2 -1
- package/dist/ShaderLibraryCore.js.map +1 -1
- package/dist/WebGL2Device.d.ts +19 -1
- package/dist/WebGL2Device.d.ts.map +1 -1
- package/dist/WebGL2Device.js +114 -37
- package/dist/WebGL2Device.js.map +1 -1
- package/dist/WebGL2StateCache.d.ts +22 -0
- package/dist/WebGL2StateCache.d.ts.map +1 -1
- package/dist/WebGL2StateCache.js +71 -42
- package/dist/WebGL2StateCache.js.map +1 -1
- package/dist/contracts/core.d.ts.map +1 -1
- package/dist/contracts/core.js +22 -2
- package/dist/contracts/core.js.map +1 -1
- package/dist/contracts/program.d.ts.map +1 -1
- package/dist/contracts/program.js +8 -1
- package/dist/contracts/program.js.map +1 -1
- package/dist/contracts/quality.d.ts +21 -0
- package/dist/contracts/quality.d.ts.map +1 -1
- package/dist/contracts/quality.js +5 -0
- package/dist/contracts/quality.js.map +1 -1
- package/dist/contracts/renderItem.d.ts +6 -0
- package/dist/contracts/renderItem.d.ts.map +1 -1
- package/dist/contracts/structuralMemo.d.ts +23 -0
- package/dist/contracts/structuralMemo.d.ts.map +1 -0
- package/dist/contracts/structuralMemo.js +81 -0
- package/dist/contracts/structuralMemo.js.map +1 -0
- package/dist/environment/EnvironmentCache.d.ts.map +1 -1
- package/dist/environment/EnvironmentCache.js +6 -1
- package/dist/environment/EnvironmentCache.js.map +1 -1
- package/dist/environment/FirstFrameGate.d.ts +23 -0
- package/dist/environment/FirstFrameGate.d.ts.map +1 -0
- package/dist/environment/FirstFrameGate.js +58 -0
- package/dist/environment/FirstFrameGate.js.map +1 -0
- package/dist/environment/Rgb9e5Cube.d.ts.map +1 -1
- package/dist/environment/Rgb9e5Cube.js +8 -4
- package/dist/environment/Rgb9e5Cube.js.map +1 -1
- package/dist/environment/probeBuild.d.ts +29 -0
- package/dist/environment/probeBuild.d.ts.map +1 -1
- package/dist/environment/probeBuild.js +124 -45
- package/dist/environment/probeBuild.js.map +1 -1
- package/dist/forward/Lighting.d.ts +43 -3
- package/dist/forward/Lighting.d.ts.map +1 -1
- package/dist/forward/Lighting.js +127 -17
- package/dist/forward/Lighting.js.map +1 -1
- package/dist/forward/contactShadowMaskStore.d.ts +41 -0
- package/dist/forward/contactShadowMaskStore.d.ts.map +1 -0
- package/dist/forward/contactShadowMaskStore.js +85 -0
- package/dist/forward/contactShadowMaskStore.js.map +1 -0
- package/dist/geometry/Shape.d.ts.map +1 -1
- package/dist/geometry/Shape.js +7 -2
- package/dist/geometry/Shape.js.map +1 -1
- package/dist/geometry/Support.d.ts +4 -0
- package/dist/geometry/Support.d.ts.map +1 -1
- package/dist/geometry/Support.js +3 -2
- package/dist/geometry/Support.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/passes/ContactShadowPass.d.ts +40 -3
- package/dist/passes/ContactShadowPass.d.ts.map +1 -1
- package/dist/passes/ContactShadowPass.js +73 -13
- package/dist/passes/ContactShadowPass.js.map +1 -1
- package/dist/passes/Prd02ContactShadowsContributor.d.ts +19 -0
- package/dist/passes/Prd02ContactShadowsContributor.d.ts.map +1 -1
- package/dist/passes/Prd02ContactShadowsContributor.js +179 -28
- package/dist/passes/Prd02ContactShadowsContributor.js.map +1 -1
- package/dist/post/v2Stages.d.ts.map +1 -1
- package/dist/post/v2Stages.js +2 -0
- package/dist/post/v2Stages.js.map +1 -1
- package/dist/program/ProgramFeatures.d.ts +6 -0
- package/dist/program/ProgramFeatures.d.ts.map +1 -1
- package/dist/program/ProgramFeatures.js +11 -5
- package/dist/program/ProgramFeatures.js.map +1 -1
- package/dist/program/ProgramGenerator.d.ts +6 -0
- package/dist/program/ProgramGenerator.d.ts.map +1 -1
- package/dist/program/ProgramGenerator.js +36 -1
- package/dist/program/ProgramGenerator.js.map +1 -1
- package/dist/program/ProgramKey.d.ts.map +1 -1
- package/dist/program/ProgramKey.js +4 -1
- package/dist/program/ProgramKey.js.map +1 -1
- package/dist/program/chunks/shadow_forward.glsl.d.ts +1 -1
- package/dist/program/chunks/shadow_forward.glsl.d.ts.map +1 -1
- package/dist/program/chunks/shadow_forward.glsl.js +4 -1
- package/dist/program/chunks/shadow_forward.glsl.js.map +1 -1
- package/dist/quality/DirectPath.d.ts +56 -0
- package/dist/quality/DirectPath.d.ts.map +1 -0
- package/dist/quality/DirectPath.js +74 -0
- package/dist/quality/DirectPath.js.map +1 -0
- package/dist/renderer/Background.d.ts +0 -5
- package/dist/renderer/Background.d.ts.map +1 -1
- package/dist/renderer/Background.js +56 -4
- package/dist/renderer/Background.js.map +1 -1
- package/dist/renderer/InstanceFrustumCull.d.ts +41 -0
- package/dist/renderer/InstanceFrustumCull.d.ts.map +1 -0
- package/dist/renderer/InstanceFrustumCull.js +138 -0
- package/dist/renderer/InstanceFrustumCull.js.map +1 -0
- package/dist/renderer/ShadowOrchestration.d.ts +2 -0
- package/dist/renderer/ShadowOrchestration.d.ts.map +1 -1
- package/dist/renderer/ShadowOrchestration.js +1 -0
- package/dist/renderer/ShadowOrchestration.js.map +1 -1
- package/dist/renderer/coreOutputPostRoute.d.ts +0 -5
- package/dist/renderer/coreOutputPostRoute.d.ts.map +1 -1
- package/dist/renderer/coreOutputPostRoute.js +12 -2
- package/dist/renderer/coreOutputPostRoute.js.map +1 -1
- package/dist/resources/InstanceBuffer.d.ts +2 -0
- package/dist/resources/InstanceBuffer.d.ts.map +1 -1
- package/dist/resources/InstanceBuffer.js +23 -0
- package/dist/resources/InstanceBuffer.js.map +1 -1
- package/dist/shaders/chunks/contact_shadow.glsl.d.ts +11 -6
- package/dist/shaders/chunks/contact_shadow.glsl.d.ts.map +1 -1
- package/dist/shaders/chunks/contact_shadow.glsl.js +41 -19
- package/dist/shaders/chunks/contact_shadow.glsl.js.map +1 -1
- package/dist/shaders/chunks/contact_shadow_mask.glsl.d.ts +12 -0
- package/dist/shaders/chunks/contact_shadow_mask.glsl.d.ts.map +1 -0
- package/dist/shaders/chunks/contact_shadow_mask.glsl.js +21 -0
- package/dist/shaders/chunks/contact_shadow_mask.glsl.js.map +1 -0
- package/dist/shaders/chunks/lighting_ibl.glsl.d.ts +6 -1
- package/dist/shaders/chunks/lighting_ibl.glsl.d.ts.map +1 -1
- package/dist/shaders/chunks/lighting_ibl.glsl.js +8 -2
- package/dist/shaders/chunks/lighting_ibl.glsl.js.map +1 -1
- package/dist/shadows/CascadeUse.d.ts +31 -0
- package/dist/shadows/CascadeUse.d.ts.map +1 -0
- package/dist/shadows/CascadeUse.js +60 -0
- package/dist/shadows/CascadeUse.js.map +1 -0
- package/dist/shadows/Prd02DepthShaderLibrary.d.ts +7 -1
- package/dist/shadows/Prd02DepthShaderLibrary.d.ts.map +1 -1
- package/dist/shadows/Prd02DepthShaderLibrary.js +12 -3
- package/dist/shadows/Prd02DepthShaderLibrary.js.map +1 -1
- package/dist/shadows/Prd02ShadowBridge.d.ts +12 -1
- package/dist/shadows/Prd02ShadowBridge.d.ts.map +1 -1
- package/dist/shadows/Prd02ShadowBridge.js +17 -5
- package/dist/shadows/Prd02ShadowBridge.js.map +1 -1
- package/dist/shadows/Prd02ShadowsContributor.d.ts +4 -0
- package/dist/shadows/Prd02ShadowsContributor.d.ts.map +1 -1
- package/dist/shadows/Prd02ShadowsContributor.js +3 -1
- package/dist/shadows/Prd02ShadowsContributor.js.map +1 -1
- package/dist/shadows/ShadowFilterTier.d.ts +51 -0
- package/dist/shadows/ShadowFilterTier.d.ts.map +1 -0
- package/dist/shadows/ShadowFilterTier.js +89 -0
- package/dist/shadows/ShadowFilterTier.js.map +1 -0
- package/dist/shadows/ShadowSystem.d.ts +15 -0
- package/dist/shadows/ShadowSystem.d.ts.map +1 -1
- package/dist/shadows/ShadowSystem.js +12 -2
- package/dist/shadows/ShadowSystem.js.map +1 -1
- package/dist/shadows/shadowLegacy.d.ts +12 -0
- package/dist/shadows/shadowLegacy.d.ts.map +1 -1
- package/dist/shadows/shadowLegacy.js +24 -0
- package/dist/shadows/shadowLegacy.js.map +1 -1
- package/dist/webgl2/BloomFusion.d.ts +31 -0
- package/dist/webgl2/BloomFusion.d.ts.map +1 -0
- package/dist/webgl2/BloomFusion.js +25 -0
- package/dist/webgl2/BloomFusion.js.map +1 -0
- package/dist/webgl2/ContextLifecycle.d.ts +14 -0
- package/dist/webgl2/ContextLifecycle.d.ts.map +1 -1
- package/dist/webgl2/ContextLifecycle.js +44 -5
- package/dist/webgl2/ContextLifecycle.js.map +1 -1
- package/dist/webgl2/LegacyPost.d.ts +30 -0
- package/dist/webgl2/LegacyPost.d.ts.map +1 -1
- package/dist/webgl2/LegacyPost.js +259 -27
- package/dist/webgl2/LegacyPost.js.map +1 -1
- package/dist/webgl2/MultiDraw.d.ts +2 -0
- package/dist/webgl2/MultiDraw.d.ts.map +1 -1
- package/dist/webgl2/MultiDraw.js +33 -7
- package/dist/webgl2/MultiDraw.js.map +1 -1
- package/dist/webgl2/Samplers.d.ts +10 -1
- package/dist/webgl2/Samplers.d.ts.map +1 -1
- package/dist/webgl2/Samplers.js +24 -1
- package/dist/webgl2/Samplers.js.map +1 -1
- package/dist/webgl2/TextureFormats.d.ts +12 -0
- package/dist/webgl2/TextureFormats.d.ts.map +1 -1
- package/dist/webgl2/TextureFormats.js +16 -0
- package/dist/webgl2/TextureFormats.js.map +1 -1
- package/package.json +3 -3
package/dist/ForwardPass.js
CHANGED
|
@@ -4,6 +4,8 @@ import { LightUniforms } from "./LightUniforms.js";
|
|
|
4
4
|
import { Material } from "./Material.js";
|
|
5
5
|
import { MaterialBinding, MaterialBindingError } from "./MaterialBinding.js";
|
|
6
6
|
import { MaterialInstance } from "./MaterialInstance.js";
|
|
7
|
+
import { PortableShaderMaterial } from "./PortableShaderMaterial.js";
|
|
8
|
+
import { cullInstancesToFrustum, MIN_CULLED_INSTANCES } from "./renderer/InstanceFrustumCull.js";
|
|
7
9
|
import { RenderDeviceError } from "./RenderDevice.js";
|
|
8
10
|
import { RenderPipeline } from "./RenderPipeline.js";
|
|
9
11
|
import { BaseRenderPass } from "./RenderPass.js";
|
|
@@ -23,7 +25,7 @@ import { instanceBufferSlot } from "./contracts/geometry.js";
|
|
|
23
25
|
import {} from "./VertexFormat.js";
|
|
24
26
|
import {} from "./ClusteredForwardLighting.js";
|
|
25
27
|
import { programCacheSlot } from "./contracts/program.js";
|
|
26
|
-
import { normalizeProgramFeatures } from "./program/ProgramFeatures.js";
|
|
28
|
+
import { normalizeProgramFeatures, PROGRAM_OUTPUT_SRGB_FEATURE } from "./program/ProgramFeatures.js";
|
|
27
29
|
import { featuresForGeometry, geometryHasSkin, materialFeatureWarning, materialRoutesToGeneratedProgram } from "./program/MaterialFeatures.js";
|
|
28
30
|
import { bindShaderFeatureUniforms, selectShaderFeatureBits } from "./program/FeatureSelect.js";
|
|
29
31
|
import { bindMaterialLobeUniforms } from "./materials/PhysicalFeatures.js";
|
|
@@ -71,8 +73,9 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
71
73
|
// `shaderCaches`) and die with the device.
|
|
72
74
|
static pipelineCaches = new WeakMap();
|
|
73
75
|
static uniformPacketPools = new WeakMap();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
// SI-3: one owner-keyed slot allocator per device, shared by every ForwardPass (opaque pass, interleaved
|
|
77
|
+
// transparent segments, mirror/probe views), so an instanced item keeps its buffer across passes and frames.
|
|
78
|
+
static instanceSlotAllocators = new WeakMap();
|
|
76
79
|
static vertexFormatIds = new WeakMap();
|
|
77
80
|
static nextVertexFormatId = 1;
|
|
78
81
|
static renderStateIds = new WeakMap();
|
|
@@ -83,6 +86,8 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
83
86
|
// execute() because drawItem() does not carry the pass context.
|
|
84
87
|
frameWidth = 0;
|
|
85
88
|
frameHeight = 0;
|
|
89
|
+
// WP-4: which view this pass renders, so the contact-shadow mask binds only to the main camera's draws (one object per frame).
|
|
90
|
+
contactMaskScope;
|
|
86
91
|
constructor(options) {
|
|
87
92
|
super(options.framePass?.name ?? "forward", [...(options.inputColorResource ? [options.inputColorResource] : []), ...(options.framePass?.reads ?? [])], options.framePass?.writes ?? ["color"]);
|
|
88
93
|
this.options = options;
|
|
@@ -92,6 +97,7 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
92
97
|
this.skinningPaletteUploads.beginFrame();
|
|
93
98
|
this.frameWidth = context.width;
|
|
94
99
|
this.frameHeight = context.height;
|
|
100
|
+
this.contactMaskScope = { device: context.device, width: context.width, height: context.height, cameraPosition: this.options.cameraPosition };
|
|
95
101
|
this.beginFramePools(context.device);
|
|
96
102
|
this.clusteredLighting = resolveForwardClusteredLighting(this.options.lights, context.width, context.height, this.options.cameraViewProjectionMatrix);
|
|
97
103
|
const flags = rendererQrFlags(context.device);
|
|
@@ -131,7 +137,8 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
131
137
|
this.clusteredLighting = null;
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
|
-
drawItem(device,
|
|
140
|
+
drawItem(device, sourceItem) {
|
|
141
|
+
let item = sourceItem;
|
|
135
142
|
// An instanced item with zero instances draws nothing (never a contract throw that blanks the frame).
|
|
136
143
|
if (item.instanceTransforms !== undefined && item.instanceTransforms.length === 0)
|
|
137
144
|
return;
|
|
@@ -170,6 +177,13 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
170
177
|
// Generated iff getShader resolved a generated program for this draw (a
|
|
171
178
|
// compile-failed fallback or an unskinnable skinned item is a legacy draw).
|
|
172
179
|
let generated = this.lastProgramFeatures !== undefined;
|
|
180
|
+
// Large instanced draws: submit only the instances whose bounding sphere meets this pass's view frustum.
|
|
181
|
+
const culled = this.cullInstancesToView(item, baseMaterial);
|
|
182
|
+
if (culled !== null) {
|
|
183
|
+
if (culled.count === 0)
|
|
184
|
+
return;
|
|
185
|
+
item = { ...item, instanceTransforms: culled.transforms, ...(culled.colors ? { instanceColors: culled.colors } : {}) };
|
|
186
|
+
}
|
|
173
187
|
if (item.instanceTransforms && baseMaterial.renderState.cullMode !== "none" && instancedItemNeedsPerInstanceCullState(item)) {
|
|
174
188
|
for (const expanded of expandInstancedRenderItem(item)) {
|
|
175
189
|
this.drawItem(device, expanded);
|
|
@@ -247,7 +261,7 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
247
261
|
applyEnvironmentFogUniforms(itemFog, item, shader, uniforms);
|
|
248
262
|
if (generated)
|
|
249
263
|
applyGeneratedFogUniforms(itemFog, shader, uniforms);
|
|
250
|
-
applyForwardShadowMapUniforms(selectForwardShadowMap(this.options.shadowMap, item, this.options.cameraViewMatrix, this.options.cameraPosition), item, shader, uniforms);
|
|
264
|
+
applyForwardShadowMapUniforms(selectForwardShadowMap(this.options.shadowMap, item, this.options.cameraViewMatrix, this.options.cameraPosition), item, shader, uniforms, this.contactMaskScope);
|
|
251
265
|
applyOutputColorSpaceUniform(this.options.outputColorSpace ?? "srgb", item, shader, uniforms);
|
|
252
266
|
applyCameraUniforms(this.options.cameraPosition, item, shader, uniforms);
|
|
253
267
|
applyAlphaCutoffUniform(item, shader, uniforms);
|
|
@@ -305,6 +319,25 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
305
319
|
}
|
|
306
320
|
}
|
|
307
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Per-instance frustum culling for large instanced items (see renderer/InstanceFrustumCull.ts). Only the main camera of this pass is used,
|
|
324
|
+
* and only for plain meshes: skinned/morphed/custom-attribute items, custom shader materials (vertex displacement) and items that opt out
|
|
325
|
+
* with `frustumCulled: false` always draw every instance.
|
|
326
|
+
*/
|
|
327
|
+
cullInstancesToView(item, baseMaterial) {
|
|
328
|
+
const transforms = item.instanceTransforms;
|
|
329
|
+
const viewProjection = this.options.cameraViewProjectionMatrix;
|
|
330
|
+
if (!(transforms instanceof Float32Array) || viewProjection === undefined || viewProjection.length < 16)
|
|
331
|
+
return null;
|
|
332
|
+
if (transforms.length < MIN_CULLED_INSTANCES * 16)
|
|
333
|
+
return null;
|
|
334
|
+
if (instanceCullExcluded(item, baseMaterial instanceof PortableShaderMaterial))
|
|
335
|
+
return null;
|
|
336
|
+
const colors = item.instanceColors;
|
|
337
|
+
if (colors !== undefined && !(colors instanceof Float32Array))
|
|
338
|
+
return null;
|
|
339
|
+
return cullInstancesToFrustum({ viewProjection, modelMatrix: item.modelMatrix, bounds: item.geometry.bounds, transforms, ...(colors ? { colors } : {}) });
|
|
340
|
+
}
|
|
308
341
|
/**
|
|
309
342
|
* Generated-path instancing (Phase 6): attribute matrices only — the
|
|
310
343
|
* `u_instanceMatrices[64]` uniform path exists solely in the frozen legacy
|
|
@@ -317,7 +350,7 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
317
350
|
return applyInstanceBinding(device, item, shader, uniforms);
|
|
318
351
|
const source = validateInstanceTransformSource(item);
|
|
319
352
|
const count = source.length / 16;
|
|
320
|
-
const slot = this.instanceSlotFor(device, count, item.instanceColors !== undefined);
|
|
353
|
+
const slot = this.instanceSlotFor(device, item.label, source, count, item.instanceColors !== undefined);
|
|
321
354
|
slot.buffer.setMatrices(source instanceof Float32Array ? source : new Float32Array(source), count);
|
|
322
355
|
if (item.instanceColors) {
|
|
323
356
|
slot.buffer.setColors(item.instanceColors instanceof Float32Array ? item.instanceColors : new Float32Array(item.instanceColors));
|
|
@@ -474,8 +507,10 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
474
507
|
if (selected[PRD02_IBL_FEATURE] !== undefined && !prd02IblEnvironmentAvailable(this.options.environmentLighting, item)) {
|
|
475
508
|
delete selected[PRD02_IBL_FEATURE];
|
|
476
509
|
}
|
|
477
|
-
//
|
|
478
|
-
//
|
|
510
|
+
// Direct render path (frame output colour space is sRGB: no post chain, no OutputPass): the generated program
|
|
511
|
+
// tone-maps and encodes its own output. Explicit "srgb" only; an unset option keeps the linear program.
|
|
512
|
+
if (this.options.outputColorSpace === "srgb")
|
|
513
|
+
selected[PROGRAM_OUTPUT_SRGB_FEATURE] = true;
|
|
479
514
|
const record = item ? featuresForGeometry({ ...mf, features: { ...mf.features, ...selected } }, (a) => geometryHasAttribute(item.geometry, a)) : { ...mf, features: { ...mf.features, ...selected } };
|
|
480
515
|
return normalizeProgramFeatures({
|
|
481
516
|
...record,
|
|
@@ -509,7 +544,7 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
509
544
|
pool.free.push(...pool.used);
|
|
510
545
|
pool.used = [];
|
|
511
546
|
}
|
|
512
|
-
|
|
547
|
+
ForwardPass.instanceSlotAllocators.get(device)?.beginPass();
|
|
513
548
|
}
|
|
514
549
|
/** A cleared per-item scratch map; recycled after the next execute begins. */
|
|
515
550
|
acquireUniformPacket(device) {
|
|
@@ -564,32 +599,105 @@ export class ForwardPass extends BaseRenderPass {
|
|
|
564
599
|
return pipeline;
|
|
565
600
|
}
|
|
566
601
|
/**
|
|
567
|
-
* Persistent attribute-matrix slot for the generated path (Phase 6): slots
|
|
568
|
-
*
|
|
569
|
-
*
|
|
602
|
+
* Persistent attribute-matrix slot for the generated path (Phase 6). SI-3: slots are keyed by the item's
|
|
603
|
+
* transform array (its owner), not by draw order inside one pass. The order-keyed cursor restarted at 0 in every
|
|
604
|
+
* ForwardPass, so the opaque pass and each transparent segment wrote different items into slot 0 and the
|
|
605
|
+
* content-compare upload skip never hit when several passes drew instanced items.
|
|
570
606
|
*/
|
|
571
|
-
instanceSlotFor(device, count, colors) {
|
|
572
|
-
|
|
573
|
-
|
|
607
|
+
instanceSlotFor(device, label, transforms, count, colors) {
|
|
608
|
+
let allocator = ForwardPass.instanceSlotAllocators.get(device);
|
|
609
|
+
if (!allocator) {
|
|
610
|
+
allocator = new InstanceSlotAllocator((capacity, withColors) => instanceBufferSlot.get(rendererQrFlags(device))(device, capacity, { colors: withColors }), (buffer) => buffer.dispose());
|
|
611
|
+
ForwardPass.instanceSlotAllocators.set(device, allocator);
|
|
612
|
+
}
|
|
613
|
+
return allocator.acquire(label ? allocator.labelKey(label) : transforms, count, colors);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* SI-3: owner-keyed pool of persistent instance buffers. The owner is the item label (with its occurrence index in
|
|
618
|
+
* the pass, so two items sharing a label keep two slots), or the transform array when the item has no label; labels
|
|
619
|
+
* survive the per-frame RenderItem rebuilds that replace arrays. `acquire(owner)` returns the slot that owner used last
|
|
620
|
+
* (re-created only when its capacity or colour layout no longer fits); a new owner takes a slot idle for at least
|
|
621
|
+
* `staleAfterPasses` pass executions, then a new slot up to `maxSlots`, then the least recently used one. Sharing a
|
|
622
|
+
* slot between two draws is still correct (GL orders the uploads), it only costs a re-upload.
|
|
623
|
+
*/
|
|
624
|
+
export class InstanceSlotAllocator {
|
|
625
|
+
create;
|
|
626
|
+
dispose;
|
|
627
|
+
maxSlots;
|
|
628
|
+
staleAfterPasses;
|
|
629
|
+
slots = [];
|
|
630
|
+
// Strong keys, bounded: an owner's entry is deleted when its slot is taken over, so it never exceeds maxSlots.
|
|
631
|
+
byOwner = new Map();
|
|
632
|
+
labelCounts = new Map();
|
|
633
|
+
pass = 0;
|
|
634
|
+
constructor(create, dispose, maxSlots = 256, staleAfterPasses = 16) {
|
|
635
|
+
this.create = create;
|
|
636
|
+
this.dispose = dispose;
|
|
637
|
+
this.maxSlots = maxSlots;
|
|
638
|
+
this.staleAfterPasses = staleAfterPasses;
|
|
639
|
+
}
|
|
640
|
+
/** One pass execution started (the idle clock). */
|
|
641
|
+
beginPass() {
|
|
642
|
+
this.pass += 1;
|
|
643
|
+
this.labelCounts.clear();
|
|
644
|
+
}
|
|
645
|
+
/** `label#n`: the n-th draw with this label in the current pass. */
|
|
646
|
+
labelKey(label) {
|
|
647
|
+
const n = this.labelCounts.get(label) ?? 0;
|
|
648
|
+
this.labelCounts.set(label, n + 1);
|
|
649
|
+
return `${label}#${n}`;
|
|
650
|
+
}
|
|
651
|
+
get size() {
|
|
652
|
+
return this.slots.length;
|
|
653
|
+
}
|
|
654
|
+
acquire(owner, count, colors) {
|
|
574
655
|
const capacity = Math.max(64, 2 ** Math.ceil(Math.log2(Math.max(1, count))));
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
656
|
+
const owned = this.byOwner.get(owner);
|
|
657
|
+
let index = owned !== undefined && this.slots[owned]?.owner === owner ? owned : -1;
|
|
658
|
+
if (index < 0) {
|
|
659
|
+
index = this.slots.findIndex((slot) => slot.owner === null || slot.lastUse <= this.pass - this.staleAfterPasses);
|
|
660
|
+
if (index < 0 && this.slots.length >= this.maxSlots) {
|
|
661
|
+
index = 0;
|
|
662
|
+
for (let i = 1; i < this.slots.length; i += 1)
|
|
663
|
+
if (this.slots[i].lastUse < this.slots[index].lastUse)
|
|
664
|
+
index = i;
|
|
665
|
+
}
|
|
579
666
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
667
|
+
if (index < 0) {
|
|
668
|
+
this.slots.push({ buffer: this.create(capacity, colors), colors, capacity, owner, lastUse: this.pass });
|
|
669
|
+
index = this.slots.length - 1;
|
|
670
|
+
}
|
|
671
|
+
const slot = this.slots[index];
|
|
672
|
+
if (slot.colors !== colors || slot.capacity < count) {
|
|
673
|
+
this.dispose(slot.buffer);
|
|
674
|
+
slot.buffer = this.create(capacity, colors);
|
|
675
|
+
slot.colors = colors;
|
|
676
|
+
slot.capacity = capacity;
|
|
677
|
+
}
|
|
678
|
+
if (slot.owner !== owner) {
|
|
679
|
+
if (slot.owner)
|
|
680
|
+
this.byOwner.delete(slot.owner);
|
|
681
|
+
slot.owner = owner;
|
|
583
682
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const slot = { buffer, colors, capacity };
|
|
587
|
-
slots[index] = slot;
|
|
588
|
-
if (slots.length > index + 64)
|
|
589
|
-
slots.length = index + 64;
|
|
683
|
+
slot.lastUse = this.pass;
|
|
684
|
+
this.byOwner.set(owner, index);
|
|
590
685
|
return slot;
|
|
591
686
|
}
|
|
592
687
|
}
|
|
688
|
+
/**
|
|
689
|
+
* Items the per-instance frustum cull must draw whole: three's `frustumCulled: false`, CPU-unknown vertex motion
|
|
690
|
+
* (skinning, morphs, custom attributes, custom shader materials), draw ranges, and SI-8 `prd10.wind` vegetation. Wind
|
|
691
|
+
* bends a plant's top by up to about 0.74 of its height while the cull sphere (1.15 x the bounds radius) covers about
|
|
692
|
+
* 0.625 of it, so a strongly bent plant at the view edge was culled while still on screen.
|
|
693
|
+
*/
|
|
694
|
+
export function instanceCullExcluded(item, customShaderMaterial) {
|
|
695
|
+
if (item.frustumCulled === false || item.skinning || item.morphTargets || item.morphWeights || item.instanceAttributes || item.drawRange)
|
|
696
|
+
return true;
|
|
697
|
+
if (customShaderMaterial)
|
|
698
|
+
return true;
|
|
699
|
+
return item.label?.startsWith("prd10.wind:") === true;
|
|
700
|
+
}
|
|
593
701
|
function lightBucket(count, max = 8) {
|
|
594
702
|
return count <= 0 ? 0 : count === 1 ? 1 : count === 2 ? 2 : count <= 4 ? 4 : max;
|
|
595
703
|
}
|
|
@@ -923,13 +1031,30 @@ function renderStateForItem(renderState, item) {
|
|
|
923
1031
|
}
|
|
924
1032
|
function instancedItemNeedsPerInstanceCullState(item) {
|
|
925
1033
|
const source = validateInstanceTransformSource(item);
|
|
926
|
-
const
|
|
927
|
-
|
|
1034
|
+
const base = toMat4Values(item.modelMatrix ?? identityMatrix(), "modelMatrix", item.label);
|
|
1035
|
+
// Allocation-free: a 10,000-instance draw runs this every pass. Only the upper 3x3 of
|
|
1036
|
+
// `base * instance` feeds the handedness test, so build it from scalars (same sums as
|
|
1037
|
+
// `multiplyMat4`, rows 0..2 / columns 0..2) instead of two arrays per instance.
|
|
928
1038
|
for (let offset = 0; offset < source.length; offset += 16) {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}
|
|
932
|
-
return
|
|
1039
|
+
if (instanceProductHasNegativeHandedness(base, source, offset))
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
function instanceProductHasNegativeHandedness(base, source, offset) {
|
|
1045
|
+
const o = offset;
|
|
1046
|
+
const c00 = base[0 * 4 + 0] * source[o] + base[1 * 4 + 0] * source[o + 1] + base[2 * 4 + 0] * source[o + 2] + base[3 * 4 + 0] * source[o + 3];
|
|
1047
|
+
const c01 = base[0 * 4 + 1] * source[o] + base[1 * 4 + 1] * source[o + 1] + base[2 * 4 + 1] * source[o + 2] + base[3 * 4 + 1] * source[o + 3];
|
|
1048
|
+
const c02 = base[0 * 4 + 2] * source[o] + base[1 * 4 + 2] * source[o + 1] + base[2 * 4 + 2] * source[o + 2] + base[3 * 4 + 2] * source[o + 3];
|
|
1049
|
+
const c10 = base[0 * 4 + 0] * source[o + 4] + base[1 * 4 + 0] * source[o + 5] + base[2 * 4 + 0] * source[o + 6] + base[3 * 4 + 0] * source[o + 7];
|
|
1050
|
+
const c11 = base[0 * 4 + 1] * source[o + 4] + base[1 * 4 + 1] * source[o + 5] + base[2 * 4 + 1] * source[o + 6] + base[3 * 4 + 1] * source[o + 7];
|
|
1051
|
+
const c12 = base[0 * 4 + 2] * source[o + 4] + base[1 * 4 + 2] * source[o + 5] + base[2 * 4 + 2] * source[o + 6] + base[3 * 4 + 2] * source[o + 7];
|
|
1052
|
+
const c20 = base[0 * 4 + 0] * source[o + 8] + base[1 * 4 + 0] * source[o + 9] + base[2 * 4 + 0] * source[o + 10] + base[3 * 4 + 0] * source[o + 11];
|
|
1053
|
+
const c21 = base[0 * 4 + 1] * source[o + 8] + base[1 * 4 + 1] * source[o + 9] + base[2 * 4 + 1] * source[o + 10] + base[3 * 4 + 1] * source[o + 11];
|
|
1054
|
+
const c22 = base[0 * 4 + 2] * source[o + 8] + base[1 * 4 + 2] * source[o + 9] + base[2 * 4 + 2] * source[o + 10] + base[3 * 4 + 2] * source[o + 11];
|
|
1055
|
+
// Column-major: matrix[0..2] = c00,c01,c02; [4..6] = c10,c11,c12; [8..10] = c20,c21,c22 (see hasNegativeHandedness).
|
|
1056
|
+
const determinant = c00 * (c11 * c22 - c21 * c12) - c10 * (c01 * c22 - c21 * c02) + c20 * (c01 * c12 - c11 * c02);
|
|
1057
|
+
return Number.isFinite(determinant) && determinant < -1e-8;
|
|
933
1058
|
}
|
|
934
1059
|
function expandInstancedRenderItem(item) {
|
|
935
1060
|
const source = validateInstanceTransformSource(item);
|
|
@@ -1316,7 +1441,7 @@ export function applyPrd02EnvironmentUniforms(environment, item, material, unifo
|
|
|
1316
1441
|
}
|
|
1317
1442
|
const packed = packA3DEnvironmentUniforms({
|
|
1318
1443
|
probe,
|
|
1319
|
-
...(env ?
|
|
1444
|
+
...(env ? prd02AmbientAndHemisphere(env) : {}),
|
|
1320
1445
|
// EnvironmentLightingOptions rotation is in turns (indirect_default);
|
|
1321
1446
|
// the PRD-02 chunk's u_envRotation is radians.
|
|
1322
1447
|
rotation: (env?.environmentMapRotation ?? 0) * Math.PI * 2,
|
|
@@ -1335,6 +1460,26 @@ export function applyPrd02EnvironmentUniforms(environment, item, material, unifo
|
|
|
1335
1460
|
expectedColorSpace: "linear"
|
|
1336
1461
|
}));
|
|
1337
1462
|
}
|
|
1463
|
+
/**
|
|
1464
|
+
* LIGHT-05/08: splits the pass ambient (which carries the hemisphere's flat mean for the programs that read only
|
|
1465
|
+
* the ambient scalar) back into the constant ambient and the directional hemisphere pair for the PRD-02 chunk.
|
|
1466
|
+
* With no hemisphere the ambient is passed through unchanged (byte-identical packet).
|
|
1467
|
+
*/
|
|
1468
|
+
export function prd02AmbientAndHemisphere(env) {
|
|
1469
|
+
const hemi = env.hemisphere;
|
|
1470
|
+
if (!hemi)
|
|
1471
|
+
return { ambient: { color: env.color, intensity: env.intensity } };
|
|
1472
|
+
const rest = [0, 0, 0];
|
|
1473
|
+
for (let i = 0; i < 3; i += 1) {
|
|
1474
|
+
const value = env.color[i] * env.intensity - (hemi.sky[i] + hemi.ground[i]) / 2;
|
|
1475
|
+
// Float residue of the add-then-subtract round trip is clamped; a real ambient is never negative.
|
|
1476
|
+
rest[i] = value > 1e-7 ? value : 0;
|
|
1477
|
+
}
|
|
1478
|
+
return {
|
|
1479
|
+
ambient: { color: rest, intensity: 1 },
|
|
1480
|
+
hemisphere: { sky: hemi.sky, ground: hemi.ground, intensity: 1, direction: hemi.direction }
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1338
1483
|
function applyProceduralEnvironmentMapUniforms(proceduralMap, item, shader, uniforms, generatedE3Conjunction = false) {
|
|
1339
1484
|
if (!proceduralMap) {
|
|
1340
1485
|
clearProceduralEnvironmentMapUniforms(shader, uniforms);
|