@driftengine/core 4.10.0 → 4.11.1
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/CHANGELOG.json +107 -0
- package/README.md +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +2 -2
- package/dist/render/ambientOcclusionPass.d.ts +3 -2
- package/dist/render/ambientOcclusionPass.js +4 -2
- package/dist/render/backend/api.d.ts +21 -1
- package/dist/render/backend/webgl2/renderer.d.ts +141 -3
- package/dist/render/backend/webgl2/renderer.js +454 -42
- package/dist/render/backend/webgpu/buffers.d.ts +5 -0
- package/dist/render/backend/webgpu/buffers.js +98 -48
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- package/dist/render/backend/webgpu/drawCommand.d.ts +2 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flatPass.js +2 -1
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +22 -1
- package/dist/render/backend/webgpu/pipelineCache.js +39 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -1
- package/dist/render/backend/webgpu/renderer.js +423 -95
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- package/dist/render/backend/webgpu/staticBundles.js +2 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -0
- package/dist/render/instances.d.ts +13 -1
- package/dist/render/instances.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/mesh.d.ts +9 -0
- package/dist/render/mesh.js +38 -14
- package/dist/render/occlusionFade.d.ts +21 -0
- package/dist/render/occlusionFade.js +27 -0
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/ambientOcclusion.js +12 -1
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +15 -0
- package/dist/render/shaders/flat/index.js +24 -4
- package/dist/render/shaders/flat/layered.d.ts +27 -0
- package/dist/render/shaders/flat/layered.js +198 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +50 -16
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +23 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +41 -19
- package/dist/render/shaders/flat/preamble.js +23 -5
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +6 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +768 -368
- package/dist/render/shaders/generated/flat.wgsl.js +791 -391
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/surfaceLayers.d.ts +107 -0
- package/dist/render/surfaceLayers.js +82 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +39 -0
- package/dist/render/transmission.d.ts +21 -0
- package/dist/render/transmission.js +21 -0
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/dist/ui/splash.d.ts +42 -2
- package/dist/ui/splash.js +78 -13
- package/package.json +6 -6
- package/src/index.ts +6 -2
- package/src/render/ambientOcclusionPass.ts +4 -1
- package/src/render/backend/api.ts +31 -0
- package/src/render/backend/webgl2/renderer.ts +519 -56
- package/src/render/backend/webgpu/buffers.ts +153 -54
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/drawCommand.ts +3 -0
- package/src/render/backend/webgpu/flatPass.ts +26 -28
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +46 -7
- package/src/render/backend/webgpu/renderer.ts +622 -164
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/staticBundles.ts +2 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +18 -1
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/mesh.ts +66 -14
- package/src/render/occlusionFade.ts +29 -0
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/ambientOcclusion.ts +12 -1
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +39 -4
- package/src/render/shaders/flat/layered.ts +198 -0
- package/src/render/shaders/flat/main.ts +50 -16
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +23 -3
- package/src/render/shaders/flat/overlay.ts +41 -19
- package/src/render/shaders/flat/preamble.ts +23 -5
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +6 -1
- package/src/render/shaders/generated/flat.wgsl.ts +791 -391
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/surfaceLayers.ts +176 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +39 -0
- package/src/render/transmission.ts +27 -0
- package/src/render/vertexPacking.ts +109 -0
- package/src/ui/splash.ts +83 -14
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { vertexPackingOf } from '../../vertexPacking.js';
|
|
2
|
+
import { markPacking, markPackingOfKey, packingKey } from './vertexFormats.js';
|
|
1
3
|
import { mat4 } from 'gl-matrix';
|
|
2
4
|
import { probeFaceRange } from '../../reflectionProbe.js';
|
|
3
5
|
import { TEMPORAL_FLICKER, TEMPORAL_HISTORY_BLEND, TemporalHistory, flickerRule, jitterOffset, jitterProjection, } from '../../temporalAa.js';
|
|
@@ -60,6 +62,11 @@ import { gpuCompressedFormats } from './compressedUpload.js';
|
|
|
60
62
|
import { GpuClothBinding, GpuClothParticles, clothStandIn } from './clothTextures.js';
|
|
61
63
|
import { MODEL_PARAM_FLOATS, eyeAxisInWorld, packModel, skinProfileDistance, } from '../../surfaceModel.js';
|
|
62
64
|
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
65
|
+
import { PROJECTION_FLOATS, packSurfaceProjection } from '../../surfaceProjection.js';
|
|
66
|
+
import { resolveTransmission } from '../../transmission.js';
|
|
67
|
+
import { resolveOcclusionFade } from '../../occlusionFade.js';
|
|
68
|
+
import { orthographicDepthSpan } from '../../lightMatrix.js';
|
|
69
|
+
import { LAYER_FLOATS, layerMaskMap, layeredProjection, layersFitBeside, packSurfaceLayers, } from '../../surfaceLayers.js';
|
|
63
70
|
import { frameReflectionsRefused, resolveFrameReflections, } from '../../frameReflections.js';
|
|
64
71
|
import { REFLECTION_SURFACE_FORMAT, ReflectionSurfacePass } from './reflectionSurfacePass.js';
|
|
65
72
|
import { validateClothBinding } from '../../clothBindingData.js';
|
|
@@ -223,6 +230,11 @@ const MAX_OVERLAYS = 64;
|
|
|
223
230
|
* and passes nobody has written yet; the cost is sixteen kilobytes.
|
|
224
231
|
*/
|
|
225
232
|
const MAX_SKY_DRAWS = 64;
|
|
233
|
+
/** The draw a preparation asks pipelines for: every option at its default. See `prepareMesh`. */
|
|
234
|
+
const PREPARED_DRAW = {};
|
|
235
|
+
/** A preparation's morph states for a mesh with no targets, and for one with some. */
|
|
236
|
+
const NOT_MORPHED = [false];
|
|
237
|
+
const MORPHED_OR_NOT = [false, true];
|
|
226
238
|
/**
|
|
227
239
|
* Whether the environment probe's permutation fits this device's per-stage binding limits.
|
|
228
240
|
*
|
|
@@ -857,7 +869,21 @@ export class WebGPURenderer {
|
|
|
857
869
|
* flight at the time, so a consumer that builds more meshes later can wait again.
|
|
858
870
|
*/
|
|
859
871
|
async ready() {
|
|
860
|
-
await Promise.all(this.
|
|
872
|
+
await Promise.all(this.pipelineCaches().map((cache) => cache.ready()));
|
|
873
|
+
}
|
|
874
|
+
/** Every distinct cache a lit draw reaches: the frame's, the canvas's, the late pass's, skin's. */
|
|
875
|
+
pipelineCaches() {
|
|
876
|
+
const caches = [...this.pipelineTargets];
|
|
877
|
+
for (const cache of [this.latePipelines, this.skinPipelines]) {
|
|
878
|
+
if (cache !== null && !caches.includes(cache))
|
|
879
|
+
caches.push(cache);
|
|
880
|
+
}
|
|
881
|
+
return caches;
|
|
882
|
+
}
|
|
883
|
+
/** Whether a lit draw whose pipeline is not built is left out while it compiles. */
|
|
884
|
+
skipCompiling(skips) {
|
|
885
|
+
for (const cache of this.pipelineCaches())
|
|
886
|
+
cache.skipsCompiling = skips;
|
|
861
887
|
}
|
|
862
888
|
presentedFrameCount = 0;
|
|
863
889
|
/**
|
|
@@ -1627,6 +1653,8 @@ export class WebGPURenderer {
|
|
|
1627
1653
|
temporalHistoryUsable = false;
|
|
1628
1654
|
aoStaging = new ArrayBuffer(AO_FRAG_SIZE);
|
|
1629
1655
|
aoFloats = new Float32Array(this.aoStaging);
|
|
1656
|
+
/** Where occlusion fades out with distance, `uFade`: none until a caller sets one. */
|
|
1657
|
+
aoFade = new Float32Array([-1, 0]);
|
|
1630
1658
|
aoBindGroup = null;
|
|
1631
1659
|
aoBlurLayout;
|
|
1632
1660
|
/**
|
|
@@ -2177,6 +2205,7 @@ export class WebGPURenderer {
|
|
|
2177
2205
|
}
|
|
2178
2206
|
filmCommand.vertexCount = geometry.vertexBuffers.length;
|
|
2179
2207
|
filmCommand.indexBuffer = geometry.indexBuffer;
|
|
2208
|
+
filmCommand.indexFormat = geometry.indexFormat;
|
|
2180
2209
|
filmCommand.indexed = true;
|
|
2181
2210
|
filmCommand.count = geometry.indexCount;
|
|
2182
2211
|
}
|
|
@@ -2189,7 +2218,7 @@ export class WebGPURenderer {
|
|
|
2189
2218
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
2190
2219
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index]);
|
|
2191
2220
|
}
|
|
2192
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
2221
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
2193
2222
|
pass.drawIndexed(geometry.indexCount);
|
|
2194
2223
|
}
|
|
2195
2224
|
}
|
|
@@ -2223,6 +2252,17 @@ export class WebGPURenderer {
|
|
|
2223
2252
|
modelMap = null;
|
|
2224
2253
|
/** The material's shading model, which picks the lit pipeline: null the standard. */
|
|
2225
2254
|
materialModel = null;
|
|
2255
|
+
/** `uWorldUv`, the material's projection packed by `packSurfaceProjection`. */
|
|
2256
|
+
projection = new Float32Array(PROJECTION_FLOATS);
|
|
2257
|
+
/** `uLayers`, the material's layers packed by `packSurfaceLayers`. */
|
|
2258
|
+
layerFloats = new Float32Array(LAYER_FLOATS);
|
|
2259
|
+
/**
|
|
2260
|
+
* `uGlassTint` as the material leaves it, its light from behind and that light's colour, which a
|
|
2261
|
+
* pane overwrites for its own draw and `releaseSeeThrough` puts back. See `transmission.ts`.
|
|
2262
|
+
*/
|
|
2263
|
+
transmission = new Float32Array([-1, -1, -1, 0]);
|
|
2264
|
+
warnedLayersModelMap = false;
|
|
2265
|
+
warnedLayersTriplanar = false;
|
|
2226
2266
|
/** Its numbers, `uModelParams`, packed by `packModel`. */
|
|
2227
2267
|
modelParams = new Float32Array(MODEL_PARAM_FLOATS);
|
|
2228
2268
|
/**
|
|
@@ -2623,7 +2663,10 @@ export class WebGPURenderer {
|
|
|
2623
2663
|
const normal = (material?.normal ?? null);
|
|
2624
2664
|
const orm = (material?.orm ?? null);
|
|
2625
2665
|
const emissiveMap = (material?.emissive ?? null);
|
|
2626
|
-
const
|
|
2666
|
+
const ownModelMap = (material?.modelMap ?? null);
|
|
2667
|
+
/* A layered material's mask, where it is a map, goes where a model's would: surfaceLayers.ts. */
|
|
2668
|
+
const layers = this.layersOf(material);
|
|
2669
|
+
const modelMap = ownModelMap ?? layerMaskMap(layers);
|
|
2627
2670
|
this.materialModel = material?.model ?? null;
|
|
2628
2671
|
const reflectivity = material?.reflectivity;
|
|
2629
2672
|
const environmentGain = material?.environmentGain;
|
|
@@ -2634,7 +2677,7 @@ export class WebGPURenderer {
|
|
|
2634
2677
|
this.writeEnvironmentDials();
|
|
2635
2678
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
2636
2679
|
const model = material?.model;
|
|
2637
|
-
warnUnpagedLightmap(model,
|
|
2680
|
+
warnUnpagedLightmap(model, ownModelMap);
|
|
2638
2681
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
2639
2682
|
this.skinScatter.setProfile(model.profile, skinProfileDistance(model, 0), skinProfileDistance(model, 1), skinProfileDistance(model, 2));
|
|
2640
2683
|
}
|
|
@@ -2682,10 +2725,26 @@ export class WebGPURenderer {
|
|
|
2682
2725
|
f[emissive + 1] = asked?.[1] ?? 1;
|
|
2683
2726
|
f[emissive + 2] = asked?.[2] ?? 1;
|
|
2684
2727
|
/* The model's numbers and whether its map is bound. See `packModel`. */
|
|
2685
|
-
packModel(material?.model ?? null,
|
|
2728
|
+
packModel(material?.model ?? null, ownModelMap !== null, this.modelParams, material?.physicalSpecular === true);
|
|
2686
2729
|
/* The first physical highlight asked for is when the lit pipelines start carrying one. */
|
|
2687
2730
|
if (material?.physicalSpecular === true)
|
|
2688
2731
|
this.enableLit('PHYSICAL_SPECULAR');
|
|
2732
|
+
/* Maps placed by the world: the first material that asks is when lit pipelines carry it. */
|
|
2733
|
+
const projection = layeredProjection(material?.projection, layers);
|
|
2734
|
+
if (projection !== (material?.projection ?? null))
|
|
2735
|
+
this.warnLayersTriplanar();
|
|
2736
|
+
packSurfaceProjection(projection, this.projection);
|
|
2737
|
+
f.set(this.projection, this.materialField('uWorldUv'));
|
|
2738
|
+
/* How much light from behind it lets through, and its colour: transmission.ts. */
|
|
2739
|
+
resolveTransmission(material, this.transmission);
|
|
2740
|
+
f.set(this.transmission, this.materialField('uGlassTint'));
|
|
2741
|
+
if (this.projection[0] > 0)
|
|
2742
|
+
this.enableLit('WORLD_UVS');
|
|
2743
|
+
/* And its layers, the first time a material blends any: layered.ts. */
|
|
2744
|
+
packSurfaceLayers(layers, this.layerFloats);
|
|
2745
|
+
f.set(this.layerFloats, this.materialField('uLayers'));
|
|
2746
|
+
if (layers !== null)
|
|
2747
|
+
this.enableLit('LAYERED');
|
|
2689
2748
|
f.set(this.modelParams, this.materialField('uModelParams'));
|
|
2690
2749
|
});
|
|
2691
2750
|
/*
|
|
@@ -2873,6 +2932,14 @@ export class WebGPURenderer {
|
|
|
2873
2932
|
this.perFrameFloats[this.materialField('uWriteMode') + 1] = Math.min(1, Math.max(-1, amount));
|
|
2874
2933
|
this.materials.dirty();
|
|
2875
2934
|
}
|
|
2935
|
+
/** Whether the draws that follow spend their own opacity on that screen door. See `renderer.ts`. */
|
|
2936
|
+
setDitherOpacity(on) {
|
|
2937
|
+
if (this.surface.lost)
|
|
2938
|
+
return;
|
|
2939
|
+
/* The third lane of `uWriteMode`, beside the fade. */
|
|
2940
|
+
this.perFrameFloats[this.materialField('uWriteMode') + 2] = on ? 1 : 0;
|
|
2941
|
+
this.materials.dirty();
|
|
2942
|
+
}
|
|
2876
2943
|
/**
|
|
2877
2944
|
* How strong the microscopic relief on the surfaces drawn next is, and how coarse.
|
|
2878
2945
|
*
|
|
@@ -4154,6 +4221,7 @@ export class WebGPURenderer {
|
|
|
4154
4221
|
const sunShadow = env === undefined ? 0 : clamp(options.sunShadow ?? 0, 0, 1);
|
|
4155
4222
|
ring.writeFloat(fragmentSlot, at('uSunShadow'), sunShadow);
|
|
4156
4223
|
ring.writeFloats(fragmentSlot, at('uLightViewProj'), env === undefined ? NO_LIGHT_MATRIX : env.lightViewProj);
|
|
4224
|
+
ring.writeFloats(fragmentSlot, at('uMovingLightViewProj'), env === undefined ? NO_LIGHT_MATRIX : this.movingLightFor(env));
|
|
4157
4225
|
ring.writeFloat(fragmentSlot, at('uShadowMapSize'), this.quality.directionalShadowMapSize);
|
|
4158
4226
|
/* Sampled only where a pass filled it — see `peelFilled` for why existing is not enough. */
|
|
4159
4227
|
ring.writeInt(fragmentSlot, at('uPeeledShadowEnabled'), this.peelFilled ? 1 : 0);
|
|
@@ -4182,6 +4250,7 @@ export class WebGPURenderer {
|
|
|
4182
4250
|
}
|
|
4183
4251
|
volumeCommand.vertexCount = mesh.vertexBuffers.length;
|
|
4184
4252
|
volumeCommand.indexBuffer = mesh.indexBuffer;
|
|
4253
|
+
volumeCommand.indexFormat = mesh.indexFormat;
|
|
4185
4254
|
volumeCommand.indexed = true;
|
|
4186
4255
|
volumeCommand.count = mesh.indexCount;
|
|
4187
4256
|
}
|
|
@@ -4191,7 +4260,7 @@ export class WebGPURenderer {
|
|
|
4191
4260
|
for (let index = 0; index < mesh.vertexBuffers.length; index++) {
|
|
4192
4261
|
pass.setVertexBuffer(index, mesh.vertexBuffers[index]);
|
|
4193
4262
|
}
|
|
4194
|
-
pass.setIndexBuffer(mesh.indexBuffer,
|
|
4263
|
+
pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);
|
|
4195
4264
|
pass.drawIndexed(mesh.indexCount);
|
|
4196
4265
|
}
|
|
4197
4266
|
}
|
|
@@ -4907,6 +4976,22 @@ export class WebGPURenderer {
|
|
|
4907
4976
|
* the texel-for-texel comparison that settled it.
|
|
4908
4977
|
*/
|
|
4909
4978
|
correctedLightViewProj = new Float32Array(16);
|
|
4979
|
+
/**
|
|
4980
|
+
* What a receiver projects through to read the sun's static layers: the environment's
|
|
4981
|
+
* `lightViewProj`, taken at `bindMeshPass` as WebGL2 takes it there.
|
|
4982
|
+
*
|
|
4983
|
+
* **It was the last shadow pass's matrix**, which is the environment's wherever both layers are
|
|
4984
|
+
* drawn with one and the moving layer's wherever they are not: the static layer was then read
|
|
4985
|
+
* through the moving layer's tight square, and a distant pillar lost its shadow on WebGPU and kept
|
|
4986
|
+
* it on WebGL2, measured on demo/dev/sunLayers.html?moving=tight.
|
|
4987
|
+
*/
|
|
4988
|
+
receiverLightViewProj = new Float32Array(16);
|
|
4989
|
+
/**
|
|
4990
|
+
* The matrix the sun's dynamic shadow pass was last drawn with, uncorrected as the lit stage
|
|
4991
|
+
* projects with, and whether one has been. See `movingLightFor`.
|
|
4992
|
+
*/
|
|
4993
|
+
movingLightViewProj = new Float32Array(16);
|
|
4994
|
+
movingLightKnown = false;
|
|
4910
4995
|
/**
|
|
4911
4996
|
* The same matrix *uncorrected*, which is what the flat shader must project with.
|
|
4912
4997
|
*
|
|
@@ -5003,6 +5088,53 @@ export class WebGPURenderer {
|
|
|
5003
5088
|
this.glassSeen = true;
|
|
5004
5089
|
this.enableLit('GLASS_SHADOWS');
|
|
5005
5090
|
}
|
|
5091
|
+
/**
|
|
5092
|
+
* A material's layers where they can be drawn: none asked, or a model's map in the mask's place,
|
|
5093
|
+
* said once, leave one layer.
|
|
5094
|
+
*/
|
|
5095
|
+
layersOf(material) {
|
|
5096
|
+
const layers = material?.layers ?? null;
|
|
5097
|
+
if (layers === null || layers.repeats.length === 0)
|
|
5098
|
+
return null;
|
|
5099
|
+
if (layersFitBeside(layers, material?.modelMap))
|
|
5100
|
+
return layers;
|
|
5101
|
+
if (!this.warnedLayersModelMap) {
|
|
5102
|
+
this.warnedLayersModelMap = true;
|
|
5103
|
+
console.warn("WebGPU: a material's layers are drawn as one: its mask is a map, read where a model's map " +
|
|
5104
|
+
"goes, and this material carries a `modelMap` too. A mask in the ORM array (`mask: 'orm'`) " +
|
|
5105
|
+
"or the vertex colour (`mask: 'vertex'`) leaves the slot free.");
|
|
5106
|
+
}
|
|
5107
|
+
return null;
|
|
5108
|
+
}
|
|
5109
|
+
/** Says once that a layered material's triplanar projection is laid on the horizontal plane. */
|
|
5110
|
+
warnLayersTriplanar() {
|
|
5111
|
+
if (this.warnedLayersTriplanar)
|
|
5112
|
+
return;
|
|
5113
|
+
this.warnedLayersTriplanar = true;
|
|
5114
|
+
console.warn("WebGPU: a layered material's triplanar projection is laid on the horizontal plane: every " +
|
|
5115
|
+
"layer's maps on three planes would be three times the reads. See `SurfaceMaterial.layers`.");
|
|
5116
|
+
}
|
|
5117
|
+
/**
|
|
5118
|
+
* What the sun's moving layer is read through: the matrix its shadow pass was last drawn with,
|
|
5119
|
+
* where the lit pipelines carry one (`MOVING_SUN`); the environment's otherwise, which every caller
|
|
5120
|
+
* drawing both layers with one matrix hands it anyway.
|
|
5121
|
+
*/
|
|
5122
|
+
movingLightFor(env) {
|
|
5123
|
+
return this.movingLightKnown && this.pipelines.litSwitches.MOVING_SUN
|
|
5124
|
+
? this.movingLightViewProj
|
|
5125
|
+
: env.lightViewProj;
|
|
5126
|
+
}
|
|
5127
|
+
/** The moving layer's own matrix on, the first time a frame draws that layer with one. */
|
|
5128
|
+
enableMovingSun(env) {
|
|
5129
|
+
if (this.pipelines.litSwitches.MOVING_SUN || !this.movingLightKnown)
|
|
5130
|
+
return;
|
|
5131
|
+
for (let k = 0; k < 16; k++) {
|
|
5132
|
+
if (this.movingLightViewProj[k] !== env.lightViewProj[k]) {
|
|
5133
|
+
this.enableLit('MOVING_SUN');
|
|
5134
|
+
return;
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5006
5138
|
/** One of the lit stage's switches on, in every cache a lit pipeline can come from. */
|
|
5007
5139
|
enableLit(feature) {
|
|
5008
5140
|
if (this.pipelines.litSwitches[feature])
|
|
@@ -5056,7 +5188,7 @@ export class WebGPURenderer {
|
|
|
5056
5188
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
5057
5189
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index]);
|
|
5058
5190
|
}
|
|
5059
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
5191
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
5060
5192
|
pass.drawIndexed(geometry.indexCount);
|
|
5061
5193
|
},
|
|
5062
5194
|
/**
|
|
@@ -5129,7 +5261,7 @@ export class WebGPURenderer {
|
|
|
5129
5261
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index]);
|
|
5130
5262
|
}
|
|
5131
5263
|
pass.setVertexBuffer(index, gpuBatch.buffer);
|
|
5132
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
5264
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
5133
5265
|
pass.drawIndexed(geometry.indexCount, count);
|
|
5134
5266
|
},
|
|
5135
5267
|
skinnedMesh: (mesh, model, palette, material, cloth) => {
|
|
@@ -5172,7 +5304,7 @@ export class WebGPURenderer {
|
|
|
5172
5304
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
5173
5305
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index]);
|
|
5174
5306
|
}
|
|
5175
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
5307
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
5176
5308
|
pass.drawIndexed(geometry.indexCount);
|
|
5177
5309
|
},
|
|
5178
5310
|
/**
|
|
@@ -5543,6 +5675,8 @@ export class WebGPURenderer {
|
|
|
5543
5675
|
this.overlayPipelines === this.pipelines
|
|
5544
5676
|
? [this.pipelines]
|
|
5545
5677
|
: [this.pipelines, this.overlayPipelines];
|
|
5678
|
+
/* A lit draw first needed inside a frame waits for its compile or is left out while it runs. */
|
|
5679
|
+
this.skipCompiling(quality.pipelineCompile === 'skip');
|
|
5546
5680
|
this.variant = flatVariant({
|
|
5547
5681
|
directionalShadows: quality.directionalShadows,
|
|
5548
5682
|
/* No `bakeReflectionProbe` here; see the field. */
|
|
@@ -6410,6 +6544,8 @@ export class WebGPURenderer {
|
|
|
6410
6544
|
present['layers'] = key.includes(':layers');
|
|
6411
6545
|
/* Nor is a dynamic mesh's split, which moves positions and normals into buffers of their own. */
|
|
6412
6546
|
present['dynamic'] = key.includes(':dynamic');
|
|
6547
|
+
/* Nor how each attribute travels, which moves every field after a packed one. */
|
|
6548
|
+
markPackingOfKey(present, key);
|
|
6413
6549
|
return present;
|
|
6414
6550
|
}
|
|
6415
6551
|
/**
|
|
@@ -6713,6 +6849,13 @@ export class WebGPURenderer {
|
|
|
6713
6849
|
present['dynamic'] = true;
|
|
6714
6850
|
key += ':dynamic';
|
|
6715
6851
|
}
|
|
6852
|
+
/*
|
|
6853
|
+
* And how each attribute travels (`vertexPacking.ts`): a field read as sixteen bits rather than
|
|
6854
|
+
* thirty-two, or not interleaved at all, moves every field after it, so the key says so.
|
|
6855
|
+
*/
|
|
6856
|
+
const packing = vertexPackingOf(data, dynamic);
|
|
6857
|
+
markPacking(present, packing);
|
|
6858
|
+
key += packingKey(packing);
|
|
6716
6859
|
const fullKey = `${this.variant}|${key}`;
|
|
6717
6860
|
/*
|
|
6718
6861
|
* **For every target this mesh can land on, which is two when the overlay is its own.**
|
|
@@ -6920,6 +7063,8 @@ export class WebGPURenderer {
|
|
|
6920
7063
|
bindMeshPass(camera, env) {
|
|
6921
7064
|
if (this.surface.lost)
|
|
6922
7065
|
return;
|
|
7066
|
+
/* The matrix every receiver projects through: the environment's, as WebGL2's. See the field. */
|
|
7067
|
+
this.receiverLightViewProj.set(env.lightViewProj);
|
|
6923
7068
|
/* Rectangles past the fixed arm's are the froxel table's, and without it nobody shades them. */
|
|
6924
7069
|
if (!this.warnedAreaOverflow &&
|
|
6925
7070
|
!this.quality.clusteredLights &&
|
|
@@ -7034,6 +7179,13 @@ export class WebGPURenderer {
|
|
|
7034
7179
|
this.surfaceFog = FOG_RECEDE;
|
|
7035
7180
|
/* Every material switch off — albedo, ORM map, emissive map, two-sided — as WebGL2 resets them. */
|
|
7036
7181
|
i.fill(0, at('uMaterialFlags'), at('uMaterialFlags') + 4);
|
|
7182
|
+
/* And no projection: maps follow the mesh's coordinates until a material asks otherwise. */
|
|
7183
|
+
f.fill(0, at('uWorldUv'), at('uWorldUv') + 4);
|
|
7184
|
+
/* Nor light through from behind. See `SurfaceMaterial.diffuseTransmission`. */
|
|
7185
|
+
resolveTransmission(null, this.transmission);
|
|
7186
|
+
f.set(this.transmission, at('uGlassTint'));
|
|
7187
|
+
/* Nor layers: one layer until a material asks for more. */
|
|
7188
|
+
f.fill(0, at('uLayers'), at('uLayers') + LAYER_FLOATS);
|
|
7037
7189
|
f[at('uCutout')] = 0;
|
|
7038
7190
|
f[at('uCutout') + 1] = 0;
|
|
7039
7191
|
this.cutoutResolveStaged = 'hard';
|
|
@@ -7106,6 +7258,7 @@ export class WebGPURenderer {
|
|
|
7106
7258
|
/* No weighting and no dither until a pass or a crossfade asks. See `setDitherFade`. */
|
|
7107
7259
|
f[at('uWriteMode')] = 0;
|
|
7108
7260
|
f[at('uWriteMode') + 1] = 0;
|
|
7261
|
+
f[at('uWriteMode') + 2] = 0;
|
|
7109
7262
|
/* 1, not 0: a multiplier's identity is one. See `setEnvironmentGain`. */
|
|
7110
7263
|
f[at('uEnvironmentDials') + 2] = 1;
|
|
7111
7264
|
/* No overlay until a draw sets one. See `setSurfaceOverlay`. */
|
|
@@ -7386,6 +7539,11 @@ export class WebGPURenderer {
|
|
|
7386
7539
|
f[at('uShadowStrength')] = env.shadowStrength;
|
|
7387
7540
|
f[at('uShadowMapSize')] = this.quality.directionalShadowMapSize;
|
|
7388
7541
|
f[at('uShadowDepthSpan')] = env.shadowDepthSpan;
|
|
7542
|
+
/* The moving layer's own matrix and span, where the lit stage reads one: `movingLightFor`. */
|
|
7543
|
+
this.enableMovingSun(env);
|
|
7544
|
+
const moving = this.movingLightFor(env);
|
|
7545
|
+
f.set(moving, at('uMovingLightViewProj'));
|
|
7546
|
+
f[at('uMovingShadowDepthSpan')] = orthographicDepthSpan(moving);
|
|
7389
7547
|
f[at('uShadowMaxDistance')] = this.quality.directionalShadowMaxDistance;
|
|
7390
7548
|
f[at('uShadowMaxSlope')] = this.quality.directionalShadowMaxSlope;
|
|
7391
7549
|
/* Sampled only where a pass filled it — see `peelFilled` for why existing is not enough. */
|
|
@@ -7569,7 +7727,7 @@ export class WebGPURenderer {
|
|
|
7569
7727
|
/** A view block: the camera, the light's matrix and the frame's wind, into `ring` at `slot`. */
|
|
7570
7728
|
writeView(ring, slot, viewProj) {
|
|
7571
7729
|
ring.writeFloats(slot, FLAT_VIEW_FIELDS.uViewProj.offset, viewProj);
|
|
7572
|
-
ring.writeFloats(slot, FLAT_VIEW_FIELDS.uLightViewProj.offset, this.
|
|
7730
|
+
ring.writeFloats(slot, FLAT_VIEW_FIELDS.uLightViewProj.offset, this.receiverLightViewProj);
|
|
7573
7731
|
/* The scene's clock, which a bone animation's instances read their moment from. */
|
|
7574
7732
|
ring.writeFloat(slot, FLAT_VIEW_FIELDS.uSceneTime.offset, this.animationTime);
|
|
7575
7733
|
const d = this.frameWind;
|
|
@@ -7750,6 +7908,153 @@ export class WebGPURenderer {
|
|
|
7750
7908
|
`TranslucentMeshOptions.tint` for why, and for the fade it was added for. */
|
|
7751
7909
|
this.submitMesh(mesh, model, options.tint ?? null, Math.min(opacity, 1), true, options);
|
|
7752
7910
|
}
|
|
7911
|
+
/**
|
|
7912
|
+
* Compile off the main thread every pipeline a draw of `mesh` in `material` takes. **The other
|
|
7913
|
+
* backend's `prepareMesh` carries the contract**; here a draw's key is `meshShape`'s, so a prepared
|
|
7914
|
+
* key is exactly the one the draw asks for, and the compiles are started as a skipping draw starts
|
|
7915
|
+
* them (`PipelineCache.lit`) whatever `pipelineCompile` says, into the frame's own caches.
|
|
7916
|
+
*/
|
|
7917
|
+
prepareMesh(mesh, material, options = {}) {
|
|
7918
|
+
if (this.surface.lost)
|
|
7919
|
+
return;
|
|
7920
|
+
this.preparing(material, (into, blend) => {
|
|
7921
|
+
const skinned = mesh.isSkinned;
|
|
7922
|
+
const eight = skinned && mesh.isSkinnedEight;
|
|
7923
|
+
for (const morphed of mesh.morph === null ? NOT_MORPHED : MORPHED_OR_NOT) {
|
|
7924
|
+
this.meshShape(mesh, blend, PREPARED_DRAW, skinned, morphed, eight, false);
|
|
7925
|
+
this.meshPipeline(blend, false, skinned, morphed, eight, false, into);
|
|
7926
|
+
}
|
|
7927
|
+
}, options);
|
|
7928
|
+
}
|
|
7929
|
+
/** `prepareMesh` for an instanced batch. See the other backend's `prepareInstanced`. */
|
|
7930
|
+
prepareInstanced(batch, material, options = {}) {
|
|
7931
|
+
if (this.surface.lost)
|
|
7932
|
+
return;
|
|
7933
|
+
this.preparing(material, (into, blend) => {
|
|
7934
|
+
const coverage = !blend && this.cutoutResolveStaged === 'coverage';
|
|
7935
|
+
this.instancedPipeline(batch.mesh, batch.animation !== null, blend, false, PREPARED_DRAW, true, 0, coverage, into);
|
|
7936
|
+
}, options);
|
|
7937
|
+
}
|
|
7938
|
+
/**
|
|
7939
|
+
* `material` set, every cache a draw in it may build into passed to `start` with whether that draw
|
|
7940
|
+
* blends, compiles started rather than built (`PipelineCache.lit`), and then no material set and
|
|
7941
|
+
* each cache back to what `pipelineCompile` says.
|
|
7942
|
+
*/
|
|
7943
|
+
preparing(material, start, options) {
|
|
7944
|
+
this.setMaterial(material);
|
|
7945
|
+
this.skipCompiling(true);
|
|
7946
|
+
try {
|
|
7947
|
+
start(this.pipelines, false);
|
|
7948
|
+
if (options.translucent === true) {
|
|
7949
|
+
start(this.pipelines, true);
|
|
7950
|
+
/* A blended draw in a reconstructing frame lands in the late pass, whose cache is its own. */
|
|
7951
|
+
if (this.latePipelines !== this.pipelines)
|
|
7952
|
+
start(this.latePipelines, true);
|
|
7953
|
+
}
|
|
7954
|
+
}
|
|
7955
|
+
finally {
|
|
7956
|
+
this.skipCompiling(this.quality.pipelineCompile === 'skip');
|
|
7957
|
+
this.setMaterial(null);
|
|
7958
|
+
}
|
|
7959
|
+
}
|
|
7960
|
+
/** What `meshShape` worked out for the mesh draw at hand. Reused, so a draw allocates no record. */
|
|
7961
|
+
drawShape = {
|
|
7962
|
+
base: '',
|
|
7963
|
+
key: '',
|
|
7964
|
+
depthWrite: true,
|
|
7965
|
+
layer: 0,
|
|
7966
|
+
adds: false,
|
|
7967
|
+
coverage: false,
|
|
7968
|
+
splits: false,
|
|
7969
|
+
modelKind: null,
|
|
7970
|
+
};
|
|
7971
|
+
/**
|
|
7972
|
+
* The key a mesh draw's pipeline is cached under, and the pipeline state that key encodes, under
|
|
7973
|
+
* the current material and these options, into `drawShape`. **The one statement of it**:
|
|
7974
|
+
* `submitMesh` draws by it and `prepareMesh` compiles by it, so the two cannot disagree about a key.
|
|
7975
|
+
*/
|
|
7976
|
+
meshShape(mesh, blend, options, skinned, morphed, eight, clothed) {
|
|
7977
|
+
const modelKind = this.materialModel?.kind ?? null;
|
|
7978
|
+
const base = mesh.key ?? 'flat:s0:u0';
|
|
7979
|
+
const keyed = `${base}${morphed ? '|morph' : ''}${skinned ? (eight ? '|skin8' : '|skin') : ''}` +
|
|
7980
|
+
`${clothed ? '|cloth' : ''}`;
|
|
7981
|
+
/*
|
|
7982
|
+
* Depth writing and the overlay layer are pipeline state on this backend, so they belong in
|
|
7983
|
+
* the key: two draws of one mesh differing only in these would otherwise share a cached
|
|
7984
|
+
* pipeline and the second would silently take the first one's depth behaviour.
|
|
7985
|
+
*
|
|
7986
|
+
* **Both suffixes are empty for the defaults**, which is what keeps the keys `createMesh`
|
|
7987
|
+
* warms — `fullKey` and `${'`'}${'$'}{fullKey}|blend${'`'}` — the ones an ordinary draw still asks for. A
|
|
7988
|
+
* caller that opts in pays one compile on its first draw, the same bargain the overlay
|
|
7989
|
+
* pipelines above already make.
|
|
7990
|
+
*/
|
|
7991
|
+
const depthWrite = options.depthWrite ?? true;
|
|
7992
|
+
const layer = Math.min(Math.max(Math.round(options.depthLayer ?? 0), 0), MAX_DEPTH_LAYER);
|
|
7993
|
+
/* Added rather than blended over: its own blend state, so its own pipeline. */
|
|
7994
|
+
const adds = blend && options.additive === true;
|
|
7995
|
+
const coverage = !blend && this.cutoutResolveStaged === 'coverage';
|
|
7996
|
+
/* Skin under the screen-space blur draws its two halves; see `keepSkinDiffuse`. */
|
|
7997
|
+
const splits = modelKind === 'skin' && !blend && this.skinSplits();
|
|
7998
|
+
const key = `${keyed}${blend ? (adds ? '|add' : '|blend') : ''}` +
|
|
7999
|
+
`${depthWrite ? '' : '|nodepth'}${layer > 0 ? `|dl${layer}` : ''}` +
|
|
8000
|
+
/* The two order-independent buffers are two formats and two blend states, so a draw into
|
|
8001
|
+
one must never share a cached pipeline with the same mesh drawn into the other. */
|
|
8002
|
+
`${this.oitMode === 'none' ? '' : `|oit${this.oitMode}`}` +
|
|
8003
|
+
/* Culling is pipeline state here: a two-sided surface culls nothing. */
|
|
8004
|
+
`${this.materialDoubleSided ? '|2s' : ''}` +
|
|
8005
|
+
/* And so is alpha-to-coverage, for a dithered cutout in a multisampled frame. */
|
|
8006
|
+
`${coverage ? '|a2c' : ''}` +
|
|
8007
|
+
/* And the material's shading model, which is the fragment stage's own switch. */
|
|
8008
|
+
`${modelKind === null ? '' : `|m:${modelKind}`}` +
|
|
8009
|
+
/* And which half of a skin it draws, under the screen-space blur. */
|
|
8010
|
+
`${splits ? '|ss' : ''}`;
|
|
8011
|
+
const shape = this.drawShape;
|
|
8012
|
+
shape.base = base;
|
|
8013
|
+
shape.key = key;
|
|
8014
|
+
shape.depthWrite = depthWrite;
|
|
8015
|
+
shape.layer = layer;
|
|
8016
|
+
shape.adds = adds;
|
|
8017
|
+
shape.coverage = coverage;
|
|
8018
|
+
shape.splits = splits;
|
|
8019
|
+
shape.modelKind = modelKind;
|
|
8020
|
+
return shape;
|
|
8021
|
+
}
|
|
8022
|
+
/**
|
|
8023
|
+
* The pipeline `drawShape` names: the one built, one built here, or none where draws skip a
|
|
8024
|
+
* compile and this one has not landed (`PipelineCache.lit`) — and none for a split skin until all
|
|
8025
|
+
* of its halves have.
|
|
8026
|
+
*/
|
|
8027
|
+
meshPipeline(blend, lands, skinned, morphed, eight, clothed,
|
|
8028
|
+
/** Which cache to build into: the one the draw's target reads, unless a preparation says. */
|
|
8029
|
+
into = null) {
|
|
8030
|
+
const { base, key, depthWrite, layer, adds, coverage, splits, modelKind } = this.drawShape;
|
|
8031
|
+
/*
|
|
8032
|
+
* **Asynchronous by default, synchronous if it has to be, and never absent.**
|
|
8033
|
+
*
|
|
8034
|
+
* `createMesh` compiles off the main thread, so a draw can legitimately arrive before the
|
|
8035
|
+
* driver has finished — a consumer that awaits `ready()` never sees this, and one that
|
|
8036
|
+
* draws immediately gets the old behaviour rather than an exception. Correctness cannot be
|
|
8037
|
+
* allowed to depend on which of those happened, so the miss builds the pipeline here and
|
|
8038
|
+
* pays the compile it was trying to avoid. That is a slow frame; the alternative is a
|
|
8039
|
+
* thrown error in the middle of one.
|
|
8040
|
+
*/
|
|
8041
|
+
const pipelines = into ?? (blend ? this.blendedPipelines(lands) : this.targetPipelines());
|
|
8042
|
+
let pipeline = pipelines.peek(key) ?? null;
|
|
8043
|
+
if (pipeline === null) {
|
|
8044
|
+
const present = this.meshPresent.get(base);
|
|
8045
|
+
if (present === undefined) {
|
|
8046
|
+
throw new Error(`WebGPU: no pipeline for ${key}; createMesh builds it`);
|
|
8047
|
+
}
|
|
8048
|
+
pipeline = flatPipeline(pipelines, this.surface.device, this.flatLayoutFor(skinned, morphed), this.variant, key, present, adds ? 'additive' : blend, skinned, morphed, depthWrite, layer, false, this.oitMode, this.materialDoubleSided, coverage, eight, clothed, modelKind, splits ? 'scene' : 'whole');
|
|
8049
|
+
}
|
|
8050
|
+
/* A split skin draws its three halves or none of them. */
|
|
8051
|
+
if (pipeline !== null &&
|
|
8052
|
+
splits &&
|
|
8053
|
+
!this.skinHalvesReady(key, base, skinned, morphed, layer, eight, clothed)) {
|
|
8054
|
+
pipeline = null;
|
|
8055
|
+
}
|
|
8056
|
+
return pipeline;
|
|
8057
|
+
}
|
|
7753
8058
|
/**
|
|
7754
8059
|
* @param blend Which of the two entry points above this came through, and **not** something to
|
|
7755
8060
|
* infer from `opacity`.
|
|
@@ -7869,7 +8174,6 @@ export class WebGPURenderer {
|
|
|
7869
8174
|
const modelKind = this.materialModel?.kind ?? null;
|
|
7870
8175
|
if (modelKind === 'eye')
|
|
7871
8176
|
this.writeEyeAxis(model, this.skinPaletteSlot >= 0 && mesh.isSkinned);
|
|
7872
|
-
const base = mesh.key ?? 'flat:s0:u0';
|
|
7873
8177
|
/*
|
|
7874
8178
|
* A rigged mesh takes the skinned vertex variant, an unrigged one the plain variant, and the
|
|
7875
8179
|
* key carries the flag so the two never share a cached pipeline. A rigged mesh whose caller
|
|
@@ -7884,38 +8188,6 @@ export class WebGPURenderer {
|
|
|
7884
8188
|
/* A cloth set and a skinned draw: the pipeline built with CLOTH_BOUND on, warmed when the
|
|
7885
8189
|
binding was made. See `createClothBinding`. */
|
|
7886
8190
|
const clothed = skinned && this.cloth !== null;
|
|
7887
|
-
const keyed = `${base}${morphed ? '|morph' : ''}${skinned ? (eight ? '|skin8' : '|skin') : ''}` +
|
|
7888
|
-
`${clothed ? '|cloth' : ''}`;
|
|
7889
|
-
/*
|
|
7890
|
-
* Depth writing and the overlay layer are pipeline state on this backend, so they belong in
|
|
7891
|
-
* the key: two draws of one mesh differing only in these would otherwise share a cached
|
|
7892
|
-
* pipeline and the second would silently take the first one's depth behaviour.
|
|
7893
|
-
*
|
|
7894
|
-
* **Both suffixes are empty for the defaults**, which is what keeps the keys `createMesh`
|
|
7895
|
-
* warms — `fullKey` and `${'`'}${'$'}{fullKey}|blend${'`'}` — the ones an ordinary draw still asks for. A
|
|
7896
|
-
* caller that opts in pays one compile on its first draw, the same bargain the overlay
|
|
7897
|
-
* pipelines above already make.
|
|
7898
|
-
*/
|
|
7899
|
-
const depthWrite = options.depthWrite ?? true;
|
|
7900
|
-
const layer = Math.min(Math.max(Math.round(options.depthLayer ?? 0), 0), MAX_DEPTH_LAYER);
|
|
7901
|
-
/* Added rather than blended over: its own blend state, so its own pipeline. */
|
|
7902
|
-
const adds = blend && options.additive === true;
|
|
7903
|
-
const coverage = !blend && this.cutoutResolveStaged === 'coverage';
|
|
7904
|
-
/* Skin under the screen-space blur draws its two halves; see `keepSkinDiffuse`. */
|
|
7905
|
-
const splits = modelKind === 'skin' && !blend && this.skinSplits();
|
|
7906
|
-
const key = `${keyed}${blend ? (adds ? '|add' : '|blend') : ''}` +
|
|
7907
|
-
`${depthWrite ? '' : '|nodepth'}${layer > 0 ? `|dl${layer}` : ''}` +
|
|
7908
|
-
/* The two order-independent buffers are two formats and two blend states, so a draw into
|
|
7909
|
-
one must never share a cached pipeline with the same mesh drawn into the other. */
|
|
7910
|
-
`${this.oitMode === 'none' ? '' : `|oit${this.oitMode}`}` +
|
|
7911
|
-
/* Culling is pipeline state here: a two-sided surface culls nothing. */
|
|
7912
|
-
`${this.materialDoubleSided ? '|2s' : ''}` +
|
|
7913
|
-
/* And so is alpha-to-coverage, for a dithered cutout in a multisampled frame. */
|
|
7914
|
-
`${coverage ? '|a2c' : ''}` +
|
|
7915
|
-
/* And the material's shading model, which is the fragment stage's own switch. */
|
|
7916
|
-
`${modelKind === null ? '' : `|m:${modelKind}`}` +
|
|
7917
|
-
/* And which half of a skin it draws, under the screen-space blur. */
|
|
7918
|
-
`${splits ? '|ss' : ''}`;
|
|
7919
8191
|
/*
|
|
7920
8192
|
* The morph uniforms, written only for a morphed draw and read from the *morphed* variant's
|
|
7921
8193
|
* field table. `FLAT_VERT_FIELDS` is the plain variant's and does not name them at all — every
|
|
@@ -7928,25 +8200,9 @@ export class WebGPURenderer {
|
|
|
7928
8200
|
this.perDraw.writeInt(slot, fields.uMorphTargetCount.offset, mesh.morph.targetCount);
|
|
7929
8201
|
this.perDraw.writeInt(slot, fields.uMorphTextureWidth.offset, mesh.morph.width);
|
|
7930
8202
|
}
|
|
7931
|
-
/*
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
* `createMesh` compiles off the main thread, so a draw can legitimately arrive before the
|
|
7935
|
-
* driver has finished — a consumer that awaits `ready()` never sees this, and one that
|
|
7936
|
-
* draws immediately gets the old behaviour rather than an exception. Correctness cannot be
|
|
7937
|
-
* allowed to depend on which of those happened, so the miss builds the pipeline here and
|
|
7938
|
-
* pays the compile it was trying to avoid. That is a slow frame; the alternative is a
|
|
7939
|
-
* thrown error in the middle of one.
|
|
7940
|
-
*/
|
|
7941
|
-
const pipelines = blend ? this.blendedPipelines(lands) : this.targetPipelines();
|
|
7942
|
-
let pipeline = pipelines.peek(key);
|
|
7943
|
-
if (pipeline === undefined) {
|
|
7944
|
-
const present = this.meshPresent.get(base);
|
|
7945
|
-
if (present === undefined) {
|
|
7946
|
-
throw new Error(`WebGPU: no pipeline for ${key}; createMesh builds it`);
|
|
7947
|
-
}
|
|
7948
|
-
pipeline = flatPipeline(pipelines, this.surface.device, this.flatLayoutFor(skinned, morphed), this.variant, key, present, adds ? 'additive' : blend, skinned, morphed, depthWrite, layer, false, this.oitMode, this.materialDoubleSided, coverage, eight, clothed, modelKind, splits ? 'scene' : 'whole');
|
|
7949
|
-
}
|
|
8203
|
+
/* Its pipeline, by the key a prepared draw compiled too: see `meshShape`. */
|
|
8204
|
+
const { base, key, layer, depthWrite, splits } = this.meshShape(mesh, blend, options, skinned, morphed, eight, clothed);
|
|
8205
|
+
let pipeline = this.meshPipeline(blend, lands, skinned, morphed, eight, clothed);
|
|
7950
8206
|
/*
|
|
7951
8207
|
* Guards the draw call only, not the restore below.
|
|
7952
8208
|
*
|
|
@@ -7963,8 +8219,9 @@ export class WebGPURenderer {
|
|
|
7963
8219
|
* close to the 256-slot budget before the gateway, the play controls, the waveform, the
|
|
7964
8220
|
* label handles and the sparkles are even counted.
|
|
7965
8221
|
*/
|
|
7966
|
-
|
|
7967
|
-
|
|
8222
|
+
/* A draw whose pipeline is still compiling is left out, its state restored all the same. */
|
|
8223
|
+
const material = pipeline === null ? null : this.materialSlotForDraw();
|
|
8224
|
+
if (pipeline !== null && material !== null) {
|
|
7968
8225
|
/*
|
|
7969
8226
|
* Record rather than issue. Every per-draw value is already in `slot` and `material` —
|
|
7970
8227
|
* the uniform rings are the state arena this design would otherwise have had to build —
|
|
@@ -7986,6 +8243,7 @@ export class WebGPURenderer {
|
|
|
7986
8243
|
}
|
|
7987
8244
|
command.vertexCount = mesh.vertexBuffers.length;
|
|
7988
8245
|
command.indexBuffer = mesh.indexBuffer;
|
|
8246
|
+
command.indexFormat = mesh.indexFormat;
|
|
7989
8247
|
command.indexed = true;
|
|
7990
8248
|
command.count = mesh.indexCount;
|
|
7991
8249
|
}
|
|
@@ -8003,7 +8261,7 @@ export class WebGPURenderer {
|
|
|
8003
8261
|
for (let index = 0; index < mesh.vertexBuffers.length; index++) {
|
|
8004
8262
|
pass.setVertexBuffer(index, mesh.vertexBuffers[index]);
|
|
8005
8263
|
}
|
|
8006
|
-
pass.setIndexBuffer(mesh.indexBuffer,
|
|
8264
|
+
pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);
|
|
8007
8265
|
pass.drawIndexed(mesh.indexCount);
|
|
8008
8266
|
}
|
|
8009
8267
|
if (splits) {
|
|
@@ -8250,6 +8508,8 @@ export class WebGPURenderer {
|
|
|
8250
8508
|
const seeAt = this.materialField('uSeeThrough');
|
|
8251
8509
|
this.perFrameFloats[seeAt] = 0;
|
|
8252
8510
|
this.perFrameFloats[seeAt + 2] = 0;
|
|
8511
|
+
/* The material's light through, over the pane's tint: transmission.ts. */
|
|
8512
|
+
this.perFrameFloats.set(this.transmission, this.materialField('uGlassTint'));
|
|
8253
8513
|
}
|
|
8254
8514
|
/**
|
|
8255
8515
|
* One draw, one material slot, one uniform slot, however many instances.
|
|
@@ -8263,6 +8523,41 @@ export class WebGPURenderer {
|
|
|
8263
8523
|
* **What it gives up** against `drawMesh` is skinning and morphing, refused where the variant
|
|
8264
8524
|
* is built, and a per-instance opacity: `opacity` here dims the whole batch.
|
|
8265
8525
|
*/
|
|
8526
|
+
/**
|
|
8527
|
+
* An instanced draw's pipeline under the current material and these options: the one built, one
|
|
8528
|
+
* built here, or none while it compiles (`PipelineCache.lit`), its key, base and model left in
|
|
8529
|
+
* `drawShape`. **The one statement of an instanced key**: `submitInstanced` draws by it and
|
|
8530
|
+
* `prepareInstanced` compiles by it.
|
|
8531
|
+
*/
|
|
8532
|
+
instancedPipeline(mesh, animated, blend, lands, options, depthWrite, layer, coverage,
|
|
8533
|
+
/** Which cache to build into: the one the draw's target reads, unless a preparation says. */
|
|
8534
|
+
into = null) {
|
|
8535
|
+
/* A batch shades by its material's model too; an eye's axis is a placement's, not a batch's. */
|
|
8536
|
+
const modelKind = this.materialModel?.kind ?? null;
|
|
8537
|
+
const base = mesh.key ?? 'flat:s0:u0';
|
|
8538
|
+
/* `|inst` in the key, for the reason every other suffix is there: an instanced pipeline binds
|
|
8539
|
+
a third vertex buffer and a different vertex module, and sharing a cache entry with the
|
|
8540
|
+
plain one would hand a draw the wrong layout. */
|
|
8541
|
+
const adds = blend && options.additive === true;
|
|
8542
|
+
const key = `${base}|inst${!animated ? '' : '|anim'}${blend ? (adds ? '|add' : '|blend') : ''}` +
|
|
8543
|
+
`${depthWrite ? '' : '|nodepth'}${layer > 0 ? `|dl${layer}` : ''}` +
|
|
8544
|
+
`${this.materialDoubleSided ? '|2s' : ''}${coverage ? '|a2c' : ''}` +
|
|
8545
|
+
`${modelKind === null ? '' : `|m:${modelKind}`}`;
|
|
8546
|
+
const shape = this.drawShape;
|
|
8547
|
+
shape.base = base;
|
|
8548
|
+
shape.key = key;
|
|
8549
|
+
shape.modelKind = modelKind;
|
|
8550
|
+
const pipelines = into ?? (blend ? this.blendedPipelines(lands) : this.targetPipelines());
|
|
8551
|
+
let pipeline = pipelines.peek(key) ?? null;
|
|
8552
|
+
if (pipeline === null) {
|
|
8553
|
+
const present = this.meshPresent.get(base);
|
|
8554
|
+
if (present === undefined) {
|
|
8555
|
+
throw new Error(`WebGPU: no mesh attributes recorded for ${base}; createMesh records them`);
|
|
8556
|
+
}
|
|
8557
|
+
pipeline = flatPipeline(pipelines, this.surface.device, !animated ? this.flatLayoutFor(false, false) : this.animatedBindGroupLayout, this.variant, key, present, adds ? 'additive' : blend, false, false, depthWrite, layer, true, 'none', this.materialDoubleSided, coverage, false, false, modelKind, 'whole', false, animated);
|
|
8558
|
+
}
|
|
8559
|
+
return pipeline;
|
|
8560
|
+
}
|
|
8266
8561
|
/**
|
|
8267
8562
|
* The next of a batch's cull slots this frame, with its cull queued against the current view; null
|
|
8268
8563
|
* when the batch has drawn from `MAX_CULL_SLOTS` views already or the frame's job ring is full,
|
|
@@ -8366,28 +8661,12 @@ export class WebGPURenderer {
|
|
|
8366
8661
|
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog) || ditheredBlend;
|
|
8367
8662
|
if (own)
|
|
8368
8663
|
this.materials.dirty();
|
|
8369
|
-
/*
|
|
8370
|
-
const
|
|
8371
|
-
const base =
|
|
8372
|
-
/*
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
const adds = blend && options.additive === true;
|
|
8376
|
-
const key = `${base}|inst${animation === null ? '' : '|anim'}${blend ? (adds ? '|add' : '|blend') : ''}` +
|
|
8377
|
-
`${depthWrite ? '' : '|nodepth'}${layer > 0 ? `|dl${layer}` : ''}` +
|
|
8378
|
-
`${this.materialDoubleSided ? '|2s' : ''}${coverage ? '|a2c' : ''}` +
|
|
8379
|
-
`${modelKind === null ? '' : `|m:${modelKind}`}`;
|
|
8380
|
-
const pipelines = blend ? this.blendedPipelines(lands) : this.targetPipelines();
|
|
8381
|
-
let pipeline = pipelines.peek(key);
|
|
8382
|
-
if (pipeline === undefined) {
|
|
8383
|
-
const present = this.meshPresent.get(base);
|
|
8384
|
-
if (present === undefined) {
|
|
8385
|
-
throw new Error(`WebGPU: no mesh attributes recorded for ${base}; createMesh records them`);
|
|
8386
|
-
}
|
|
8387
|
-
pipeline = flatPipeline(pipelines, this.surface.device, animation === null ? this.flatLayoutFor(false, false) : this.animatedBindGroupLayout, this.variant, key, present, adds ? 'additive' : blend, false, false, depthWrite, layer, true, 'none', this.materialDoubleSided, coverage, false, false, modelKind, 'whole', false, animation !== null);
|
|
8388
|
-
}
|
|
8389
|
-
const material = this.materialSlotForDraw();
|
|
8390
|
-
if (material !== null) {
|
|
8664
|
+
/* Its pipeline, by the key a prepared batch compiled too: see `instancedPipeline`. */
|
|
8665
|
+
const pipeline = this.instancedPipeline(mesh, animation !== null, blend, lands, options, depthWrite, layer, coverage);
|
|
8666
|
+
const { base, key, modelKind } = this.drawShape;
|
|
8667
|
+
/* A batch whose pipeline is still compiling is left out, as a mesh is. */
|
|
8668
|
+
const material = pipeline === null ? null : this.materialSlotForDraw();
|
|
8669
|
+
if (pipeline !== null && material !== null) {
|
|
8391
8670
|
const command = blend
|
|
8392
8671
|
? this.recordBlended(0, lands)
|
|
8393
8672
|
: this.recordDraw(0, this.currentTarget());
|
|
@@ -8409,6 +8688,7 @@ export class WebGPURenderer {
|
|
|
8409
8688
|
command.vertexBuffers[index] = instanceBuffer;
|
|
8410
8689
|
command.vertexCount = mesh.vertexBuffers.length + 1;
|
|
8411
8690
|
command.indexBuffer = mesh.indexBuffer;
|
|
8691
|
+
command.indexFormat = mesh.indexFormat;
|
|
8412
8692
|
command.indexed = true;
|
|
8413
8693
|
command.count = mesh.indexCount;
|
|
8414
8694
|
command.instances = count;
|
|
@@ -8427,7 +8707,7 @@ export class WebGPURenderer {
|
|
|
8427
8707
|
pass.setVertexBuffer(index, mesh.vertexBuffers[index]);
|
|
8428
8708
|
}
|
|
8429
8709
|
pass.setVertexBuffer(index, instanceBuffer);
|
|
8430
|
-
pass.setIndexBuffer(mesh.indexBuffer,
|
|
8710
|
+
pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);
|
|
8431
8711
|
if (cut === null)
|
|
8432
8712
|
pass.drawIndexed(mesh.indexCount, count);
|
|
8433
8713
|
else
|
|
@@ -8628,6 +8908,11 @@ export class WebGPURenderer {
|
|
|
8628
8908
|
if (view === null)
|
|
8629
8909
|
return false;
|
|
8630
8910
|
this.lightViewProj.set(lightViewProj);
|
|
8911
|
+
/* The moving layer is read through the matrix it is drawn with: see `movingLightFor`. */
|
|
8912
|
+
if (dynamic) {
|
|
8913
|
+
this.movingLightViewProj.set(lightViewProj);
|
|
8914
|
+
this.movingLightKnown = true;
|
|
8915
|
+
}
|
|
8631
8916
|
this.reopenViews();
|
|
8632
8917
|
mat4.multiply(this.correctedLightViewProj, SHADOW_CLIP_CORRECTION, lightViewProj);
|
|
8633
8918
|
this.shadowLayerIsPeel = peel;
|
|
@@ -8912,11 +9197,14 @@ export class WebGPURenderer {
|
|
|
8912
9197
|
this.lateViewBlock.slot = list.viewOffset(0);
|
|
8913
9198
|
this.materials.dirty();
|
|
8914
9199
|
this.staticRecording = recording;
|
|
9200
|
+
/* A recorded list keeps what it recorded, so every draw of it waits for its pipeline. */
|
|
9201
|
+
this.skipCompiling(false);
|
|
8915
9202
|
try {
|
|
8916
9203
|
this.sceneCasterMaterial = undefined;
|
|
8917
9204
|
replayStaticDraws(list, this.sceneCasterSink);
|
|
8918
9205
|
}
|
|
8919
9206
|
finally {
|
|
9207
|
+
this.skipCompiling(this.quality.pipelineCompile === 'skip');
|
|
8920
9208
|
this.staticRecording = null;
|
|
8921
9209
|
this.perDraw = perDraw;
|
|
8922
9210
|
this.passRing = passRing;
|
|
@@ -9186,7 +9474,7 @@ export class WebGPURenderer {
|
|
|
9186
9474
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
9187
9475
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index]);
|
|
9188
9476
|
}
|
|
9189
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
9477
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
9190
9478
|
pass.drawIndexed(geometry.indexCount);
|
|
9191
9479
|
},
|
|
9192
9480
|
instanced: (batch, data, material, glass) => {
|
|
@@ -9220,7 +9508,7 @@ export class WebGPURenderer {
|
|
|
9220
9508
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index]);
|
|
9221
9509
|
}
|
|
9222
9510
|
pass.setVertexBuffer(index, gpuBatch.buffer);
|
|
9223
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
9511
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
9224
9512
|
pass.drawIndexed(geometry.indexCount, count);
|
|
9225
9513
|
},
|
|
9226
9514
|
skinnedMesh: (mesh, model, palette, _material, glass) => {
|
|
@@ -9248,7 +9536,7 @@ export class WebGPURenderer {
|
|
|
9248
9536
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
9249
9537
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index]);
|
|
9250
9538
|
}
|
|
9251
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
9539
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
9252
9540
|
pass.drawIndexed(geometry.indexCount);
|
|
9253
9541
|
},
|
|
9254
9542
|
};
|
|
@@ -11377,6 +11665,7 @@ export class WebGPURenderer {
|
|
|
11377
11665
|
/* Uncorrected, for the reason the shaft's own copy of this line carries: the shader does its
|
|
11378
11666
|
own `* 0.5 + 0.5`, and correcting the matrix as well doubles it. */
|
|
11379
11667
|
f.set(env?.lightViewProj ?? this.mediumIdentity, at('uLightViewProj'));
|
|
11668
|
+
f.set((env === null ? this.mediumIdentity : this.movingLightFor(env)), at('uMovingLightViewProj'));
|
|
11380
11669
|
ints[at('uPeeledShadowEnabled')] = this.peelView === null || !this.peelFilled ? 0 : 1;
|
|
11381
11670
|
device.queue.writeBuffer(this.mediumUniforms, 0, this.mediumStaging);
|
|
11382
11671
|
const g = this.mediumUpsampleFloats;
|
|
@@ -11432,6 +11721,7 @@ export class WebGPURenderer {
|
|
|
11432
11721
|
const f = this.aoFloats;
|
|
11433
11722
|
const at = (name) => this.postField(AO_FRAG_FIELDS, name);
|
|
11434
11723
|
f[at('uRadius')] = this.quality.ambientOcclusionRadius;
|
|
11724
|
+
f.set(this.aoFade, at('uFade'));
|
|
11435
11725
|
f.set(this.aoProjScale, at('uProjScale'));
|
|
11436
11726
|
f.set(this.aoInvProjection, at('uInvProjection'));
|
|
11437
11727
|
device.queue.writeBuffer(this.aoUniforms, 0, this.aoStaging);
|
|
@@ -11778,6 +12068,9 @@ export class WebGPURenderer {
|
|
|
11778
12068
|
const key = `${frameKey}|rs`;
|
|
11779
12069
|
const pipeline = this.pipelines.peek(key) ??
|
|
11780
12070
|
flatPipeline(this.pipelines, this.surface.device, animation === null ? this.flatLayoutFor(skinned, morphed) : this.animatedBindGroupLayout, this.variant, key, present, false, skinned, morphed, true, layer, instanceBuffer !== null, 'none', this.materialDoubleSided, false, eight, clothed, modelKind, 'whole', true, animation !== null);
|
|
12071
|
+
/* Still compiling where draws skip: the probe stands in for this surface until it lands. */
|
|
12072
|
+
if (pipeline === null)
|
|
12073
|
+
return;
|
|
11781
12074
|
/* A static list's surface halves go into its own pool, beside its draws. */
|
|
11782
12075
|
const command = this.staticRecording === null ? surface.take() : takeRecorded(this.staticRecording.surface);
|
|
11783
12076
|
command.pipeline = pipeline;
|
|
@@ -11798,6 +12091,7 @@ export class WebGPURenderer {
|
|
|
11798
12091
|
command.vertexBuffers[index++] = instanceBuffer;
|
|
11799
12092
|
command.vertexCount = index;
|
|
11800
12093
|
command.indexBuffer = mesh.indexBuffer;
|
|
12094
|
+
command.indexFormat = mesh.indexFormat;
|
|
11801
12095
|
command.indexed = true;
|
|
11802
12096
|
command.count = mesh.indexCount;
|
|
11803
12097
|
command.instances = instances;
|
|
@@ -11809,6 +12103,30 @@ export class WebGPURenderer {
|
|
|
11809
12103
|
* light alone and its colour alone into the blur's targets and test for the depth its frame half
|
|
11810
12104
|
* wrote.
|
|
11811
12105
|
*/
|
|
12106
|
+
/**
|
|
12107
|
+
* Whether a split skin draw's two kept halves have pipelines, starting the compile of either that
|
|
12108
|
+
* has not: a skin drawn without them would lose its diffuse light for the frames they take, so a
|
|
12109
|
+
* split skin draws all three halves or none. See `PipelineCache.lit`.
|
|
12110
|
+
*/
|
|
12111
|
+
skinHalvesReady(frameKey, base, skinned, morphed, layer, eight, clothed) {
|
|
12112
|
+
let ready = true;
|
|
12113
|
+
for (const half of SKIN_KEPT_HALVES) {
|
|
12114
|
+
const pipeline = this.skinHalfPipeline(half, frameKey, base, skinned, morphed, layer, eight, clothed);
|
|
12115
|
+
if (pipeline === null)
|
|
12116
|
+
ready = false;
|
|
12117
|
+
}
|
|
12118
|
+
return ready;
|
|
12119
|
+
}
|
|
12120
|
+
/** One kept half's pipeline, or none where it is still compiling: see `PipelineCache.lit`. */
|
|
12121
|
+
skinHalfPipeline(half, frameKey, base, skinned, morphed, layer, eight, clothed) {
|
|
12122
|
+
const pipelines = this.skinPipelines;
|
|
12123
|
+
const present = this.meshPresent.get(base);
|
|
12124
|
+
if (pipelines === null || present === undefined)
|
|
12125
|
+
return null;
|
|
12126
|
+
const key = `${frameKey}|${half === 'diffuse' ? 'sd' : 'sa'}`;
|
|
12127
|
+
return (pipelines.peek(key) ??
|
|
12128
|
+
flatPipeline(pipelines, this.surface.device, this.flatLayoutFor(skinned, morphed), this.variant, key, present, false, skinned, morphed, true, layer, false, 'none', this.materialDoubleSided, false, eight, clothed, 'skin', half));
|
|
12129
|
+
}
|
|
11812
12130
|
keepSkinDiffuse(mesh, frameKey, base, skinned, morphed, deltas, layer, eight, clothed, slot, material) {
|
|
11813
12131
|
/* A skin's halves are not bundled: a static list holding one replays draw by draw. */
|
|
11814
12132
|
if (this.staticRecording !== null) {
|
|
@@ -11823,9 +12141,10 @@ export class WebGPURenderer {
|
|
|
11823
12141
|
const bindGroup = this.flatBindGroupFor(skinned, morphed, deltas);
|
|
11824
12142
|
/* The light alone, which the blur spreads, then the colour alone, which it is multiplied by. */
|
|
11825
12143
|
for (const half of SKIN_KEPT_HALVES) {
|
|
11826
|
-
const
|
|
11827
|
-
|
|
11828
|
-
|
|
12144
|
+
const pipeline = this.skinHalfPipeline(half, frameKey, base, skinned, morphed, layer, eight, clothed);
|
|
12145
|
+
/* Both landed before the draw was let through: see `skinHalvesReady`. */
|
|
12146
|
+
if (pipeline === null)
|
|
12147
|
+
return;
|
|
11829
12148
|
const command = half === 'diffuse' ? skin.take() : skin.takeAlbedo();
|
|
11830
12149
|
command.pipeline = pipeline;
|
|
11831
12150
|
command.bindGroup = bindGroup;
|
|
@@ -11839,6 +12158,7 @@ export class WebGPURenderer {
|
|
|
11839
12158
|
}
|
|
11840
12159
|
command.vertexCount = mesh.vertexBuffers.length;
|
|
11841
12160
|
command.indexBuffer = mesh.indexBuffer;
|
|
12161
|
+
command.indexFormat = mesh.indexFormat;
|
|
11842
12162
|
command.indexed = true;
|
|
11843
12163
|
command.count = mesh.indexCount;
|
|
11844
12164
|
command.instances = 1;
|
|
@@ -12277,6 +12597,7 @@ export class WebGPURenderer {
|
|
|
12277
12597
|
command.offsetCount = 0;
|
|
12278
12598
|
command.vertexCount = 0;
|
|
12279
12599
|
command.indexBuffer = null;
|
|
12600
|
+
command.indexFormat = 'uint32';
|
|
12280
12601
|
command.indexed = false;
|
|
12281
12602
|
command.instances = 1;
|
|
12282
12603
|
command.indirect = null;
|
|
@@ -12344,7 +12665,7 @@ export class WebGPURenderer {
|
|
|
12344
12665
|
pass.setVertexBuffer(i, buffer);
|
|
12345
12666
|
}
|
|
12346
12667
|
if (command.indexed && command.indexBuffer !== null) {
|
|
12347
|
-
pass.setIndexBuffer(command.indexBuffer,
|
|
12668
|
+
pass.setIndexBuffer(command.indexBuffer, command.indexFormat);
|
|
12348
12669
|
if (command.indirect !== null)
|
|
12349
12670
|
pass.drawIndexedIndirect(command.indirect, 0);
|
|
12350
12671
|
else
|
|
@@ -13992,6 +14313,13 @@ export class WebGPURenderer {
|
|
|
13992
14313
|
this.exposurePass = new ExposurePass(this.surface.device, this.pipelines, this.postSampler);
|
|
13993
14314
|
this.rebuildRushBindGroup();
|
|
13994
14315
|
}
|
|
14316
|
+
/**
|
|
14317
|
+
* Where ambient occlusion fades out with distance. **The other backend's
|
|
14318
|
+
* `setAmbientOcclusionFade` carries the reasoning**; this is the same setter over the same state.
|
|
14319
|
+
*/
|
|
14320
|
+
setAmbientOcclusionFade(distance, radius) {
|
|
14321
|
+
resolveOcclusionFade(distance, radius, this.aoFade);
|
|
14322
|
+
}
|
|
13995
14323
|
/**
|
|
13996
14324
|
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
13997
14325
|
* to take. **The other backend's `setDepthOfField` carries the reasoning**, whole: the
|