@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.ts';
|
|
2
|
+
import { markPacking, markPackingOfKey, packingKey } from './vertexFormats.ts';
|
|
1
3
|
import { mat4 } from 'gl-matrix';
|
|
2
4
|
import type { ProbeBakeOptions } from '../../reflectionProbe.ts';
|
|
3
5
|
import { probeFaceRange } from '../../reflectionProbe.ts';
|
|
@@ -57,7 +59,7 @@ import type {
|
|
|
57
59
|
SkyColors,
|
|
58
60
|
TranslucentMeshOptions,
|
|
59
61
|
} from '../webgl2/renderer.ts';
|
|
60
|
-
import type { IncrementalMeshHandle, MeshOptions } from '../api.ts';
|
|
62
|
+
import type { IncrementalMeshHandle, MeshOptions, PrepareOptions } from '../api.ts';
|
|
61
63
|
import { resolveAtmosphere, type ResolvedAtmosphere } from '../../atmosphere.ts';
|
|
62
64
|
import { AMBIENT_SH_FLOATS, packAmbientSH } from '../../ambientHarmonics.ts';
|
|
63
65
|
import {
|
|
@@ -194,6 +196,18 @@ import {
|
|
|
194
196
|
packSurfaceOverlay,
|
|
195
197
|
type SurfaceOverlay,
|
|
196
198
|
} from '../../surfaceOverlay.ts';
|
|
199
|
+
import { PROJECTION_FLOATS, packSurfaceProjection } from '../../surfaceProjection.ts';
|
|
200
|
+
import { resolveTransmission } from '../../transmission.ts';
|
|
201
|
+
import { resolveOcclusionFade } from '../../occlusionFade.ts';
|
|
202
|
+
import { orthographicDepthSpan } from '../../lightMatrix.ts';
|
|
203
|
+
import {
|
|
204
|
+
LAYER_FLOATS,
|
|
205
|
+
layerMaskMap,
|
|
206
|
+
layeredProjection,
|
|
207
|
+
layersFitBeside,
|
|
208
|
+
packSurfaceLayers,
|
|
209
|
+
} from '../../surfaceLayers.ts';
|
|
210
|
+
import type { SurfaceLayers } from '../../surfaceLayers.ts';
|
|
197
211
|
import {
|
|
198
212
|
frameReflectionsRefused,
|
|
199
213
|
resolveFrameReflections,
|
|
@@ -750,6 +764,12 @@ const MAX_OVERLAYS = 64;
|
|
|
750
764
|
*/
|
|
751
765
|
const MAX_SKY_DRAWS = 64;
|
|
752
766
|
|
|
767
|
+
/** The draw a preparation asks pipelines for: every option at its default. See `prepareMesh`. */
|
|
768
|
+
const PREPARED_DRAW: TranslucentMeshOptions = {};
|
|
769
|
+
/** A preparation's morph states for a mesh with no targets, and for one with some. */
|
|
770
|
+
const NOT_MORPHED: readonly boolean[] = [false];
|
|
771
|
+
const MORPHED_OR_NOT: readonly boolean[] = [false, true];
|
|
772
|
+
|
|
753
773
|
/**
|
|
754
774
|
* Whether the environment probe's permutation fits this device's per-stage binding limits.
|
|
755
775
|
*
|
|
@@ -1530,7 +1550,21 @@ export class WebGPURenderer implements RendererApi {
|
|
|
1530
1550
|
* flight at the time, so a consumer that builds more meshes later can wait again.
|
|
1531
1551
|
*/
|
|
1532
1552
|
async ready(): Promise<void> {
|
|
1533
|
-
await Promise.all(this.
|
|
1553
|
+
await Promise.all(this.pipelineCaches().map((cache) => cache.ready()));
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
/** Every distinct cache a lit draw reaches: the frame's, the canvas's, the late pass's, skin's. */
|
|
1557
|
+
private pipelineCaches(): PipelineCache[] {
|
|
1558
|
+
const caches = [...this.pipelineTargets];
|
|
1559
|
+
for (const cache of [this.latePipelines, this.skinPipelines]) {
|
|
1560
|
+
if (cache !== null && !caches.includes(cache)) caches.push(cache);
|
|
1561
|
+
}
|
|
1562
|
+
return caches;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
/** Whether a lit draw whose pipeline is not built is left out while it compiles. */
|
|
1566
|
+
private skipCompiling(skips: boolean): void {
|
|
1567
|
+
for (const cache of this.pipelineCaches()) cache.skipsCompiling = skips;
|
|
1534
1568
|
}
|
|
1535
1569
|
|
|
1536
1570
|
private presentedFrameCount = 0;
|
|
@@ -2327,6 +2361,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2327
2361
|
|
|
2328
2362
|
private readonly aoStaging = new ArrayBuffer(AO_FRAG_SIZE);
|
|
2329
2363
|
private readonly aoFloats = new Float32Array(this.aoStaging);
|
|
2364
|
+
/** Where occlusion fades out with distance, `uFade`: none until a caller sets one. */
|
|
2365
|
+
private readonly aoFade = new Float32Array([-1, 0]);
|
|
2330
2366
|
private aoBindGroup: GPUBindGroup | null = null;
|
|
2331
2367
|
private readonly aoBlurLayout: GPUBindGroupLayout;
|
|
2332
2368
|
/**
|
|
@@ -2927,6 +2963,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2927
2963
|
}
|
|
2928
2964
|
filmCommand.vertexCount = geometry.vertexBuffers.length;
|
|
2929
2965
|
filmCommand.indexBuffer = geometry.indexBuffer;
|
|
2966
|
+
filmCommand.indexFormat = geometry.indexFormat;
|
|
2930
2967
|
filmCommand.indexed = true;
|
|
2931
2968
|
filmCommand.count = geometry.indexCount;
|
|
2932
2969
|
} else {
|
|
@@ -2937,7 +2974,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2937
2974
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
2938
2975
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index] as GPUBuffer);
|
|
2939
2976
|
}
|
|
2940
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
2977
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
2941
2978
|
pass.drawIndexed(geometry.indexCount);
|
|
2942
2979
|
}
|
|
2943
2980
|
}
|
|
@@ -2974,6 +3011,17 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2974
3011
|
private modelMap: GpuSurfaceTexture | null = null;
|
|
2975
3012
|
/** The material's shading model, which picks the lit pipeline: null the standard. */
|
|
2976
3013
|
private materialModel: SurfaceModel | null = null;
|
|
3014
|
+
/** `uWorldUv`, the material's projection packed by `packSurfaceProjection`. */
|
|
3015
|
+
private readonly projection = new Float32Array(PROJECTION_FLOATS);
|
|
3016
|
+
/** `uLayers`, the material's layers packed by `packSurfaceLayers`. */
|
|
3017
|
+
private readonly layerFloats = new Float32Array(LAYER_FLOATS);
|
|
3018
|
+
/**
|
|
3019
|
+
* `uGlassTint` as the material leaves it, its light from behind and that light's colour, which a
|
|
3020
|
+
* pane overwrites for its own draw and `releaseSeeThrough` puts back. See `transmission.ts`.
|
|
3021
|
+
*/
|
|
3022
|
+
private readonly transmission = new Float32Array([-1, -1, -1, 0]);
|
|
3023
|
+
private warnedLayersModelMap = false;
|
|
3024
|
+
private warnedLayersTriplanar = false;
|
|
2977
3025
|
/** Its numbers, `uModelParams`, packed by `packModel`. */
|
|
2978
3026
|
private readonly modelParams = new Float32Array(MODEL_PARAM_FLOATS);
|
|
2979
3027
|
/**
|
|
@@ -3448,7 +3496,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3448
3496
|
const normal = (material?.normal ?? null) as GpuSurfaceTexture | null;
|
|
3449
3497
|
const orm = (material?.orm ?? null) as GpuSurfaceTexture | null;
|
|
3450
3498
|
const emissiveMap = (material?.emissive ?? null) as GpuSurfaceTexture | null;
|
|
3451
|
-
const
|
|
3499
|
+
const ownModelMap = (material?.modelMap ?? null) as GpuSurfaceTexture | null;
|
|
3500
|
+
/* A layered material's mask, where it is a map, goes where a model's would: surfaceLayers.ts. */
|
|
3501
|
+
const layers = this.layersOf(material);
|
|
3502
|
+
const modelMap = ownModelMap ?? layerMaskMap(layers);
|
|
3452
3503
|
this.materialModel = material?.model ?? null;
|
|
3453
3504
|
const reflectivity = material?.reflectivity;
|
|
3454
3505
|
const environmentGain = material?.environmentGain;
|
|
@@ -3459,7 +3510,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3459
3510
|
this.writeEnvironmentDials();
|
|
3460
3511
|
/* What the blur spreads this skin by: the last material to name a profile says. */
|
|
3461
3512
|
const model = material?.model;
|
|
3462
|
-
warnUnpagedLightmap(model,
|
|
3513
|
+
warnUnpagedLightmap(model, ownModelMap);
|
|
3463
3514
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
3464
3515
|
this.skinScatter.setProfile(
|
|
3465
3516
|
model.profile,
|
|
@@ -3518,12 +3569,25 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3518
3569
|
/* The model's numbers and whether its map is bound. See `packModel`. */
|
|
3519
3570
|
packModel(
|
|
3520
3571
|
material?.model ?? null,
|
|
3521
|
-
|
|
3572
|
+
ownModelMap !== null,
|
|
3522
3573
|
this.modelParams,
|
|
3523
3574
|
material?.physicalSpecular === true,
|
|
3524
3575
|
);
|
|
3525
3576
|
/* The first physical highlight asked for is when the lit pipelines start carrying one. */
|
|
3526
3577
|
if (material?.physicalSpecular === true) this.enableLit('PHYSICAL_SPECULAR');
|
|
3578
|
+
/* Maps placed by the world: the first material that asks is when lit pipelines carry it. */
|
|
3579
|
+
const projection = layeredProjection(material?.projection, layers);
|
|
3580
|
+
if (projection !== (material?.projection ?? null)) this.warnLayersTriplanar();
|
|
3581
|
+
packSurfaceProjection(projection, this.projection);
|
|
3582
|
+
f.set(this.projection, this.materialField('uWorldUv'));
|
|
3583
|
+
/* How much light from behind it lets through, and its colour: transmission.ts. */
|
|
3584
|
+
resolveTransmission(material, this.transmission);
|
|
3585
|
+
f.set(this.transmission, this.materialField('uGlassTint'));
|
|
3586
|
+
if ((this.projection[0] as number) > 0) this.enableLit('WORLD_UVS');
|
|
3587
|
+
/* And its layers, the first time a material blends any: layered.ts. */
|
|
3588
|
+
packSurfaceLayers(layers, this.layerFloats);
|
|
3589
|
+
f.set(this.layerFloats, this.materialField('uLayers'));
|
|
3590
|
+
if (layers !== null) this.enableLit('LAYERED');
|
|
3527
3591
|
f.set(this.modelParams, this.materialField('uModelParams'));
|
|
3528
3592
|
});
|
|
3529
3593
|
/*
|
|
@@ -3731,6 +3795,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3731
3795
|
this.materials.dirty();
|
|
3732
3796
|
}
|
|
3733
3797
|
|
|
3798
|
+
/** Whether the draws that follow spend their own opacity on that screen door. See `renderer.ts`. */
|
|
3799
|
+
setDitherOpacity(on: boolean): void {
|
|
3800
|
+
if (this.surface.lost) return;
|
|
3801
|
+
/* The third lane of `uWriteMode`, beside the fade. */
|
|
3802
|
+
this.perFrameFloats[this.materialField('uWriteMode') + 2] = on ? 1 : 0;
|
|
3803
|
+
this.materials.dirty();
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3734
3806
|
/**
|
|
3735
3807
|
* How strong the microscopic relief on the surfaces drawn next is, and how coarse.
|
|
3736
3808
|
*
|
|
@@ -5228,6 +5300,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5228
5300
|
at('uLightViewProj'),
|
|
5229
5301
|
env === undefined ? NO_LIGHT_MATRIX : (env.lightViewProj as Float32Array),
|
|
5230
5302
|
);
|
|
5303
|
+
ring.writeFloats(
|
|
5304
|
+
fragmentSlot,
|
|
5305
|
+
at('uMovingLightViewProj'),
|
|
5306
|
+
env === undefined ? NO_LIGHT_MATRIX : (this.movingLightFor(env) as Float32Array),
|
|
5307
|
+
);
|
|
5231
5308
|
ring.writeFloat(fragmentSlot, at('uShadowMapSize'), this.quality.directionalShadowMapSize);
|
|
5232
5309
|
/* Sampled only where a pass filled it — see `peelFilled` for why existing is not enough. */
|
|
5233
5310
|
ring.writeInt(fragmentSlot, at('uPeeledShadowEnabled'), this.peelFilled ? 1 : 0);
|
|
@@ -5267,6 +5344,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5267
5344
|
}
|
|
5268
5345
|
volumeCommand.vertexCount = mesh.vertexBuffers.length;
|
|
5269
5346
|
volumeCommand.indexBuffer = mesh.indexBuffer;
|
|
5347
|
+
volumeCommand.indexFormat = mesh.indexFormat;
|
|
5270
5348
|
volumeCommand.indexed = true;
|
|
5271
5349
|
volumeCommand.count = mesh.indexCount;
|
|
5272
5350
|
} else {
|
|
@@ -5275,7 +5353,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5275
5353
|
for (let index = 0; index < mesh.vertexBuffers.length; index++) {
|
|
5276
5354
|
pass.setVertexBuffer(index, mesh.vertexBuffers[index] as GPUBuffer);
|
|
5277
5355
|
}
|
|
5278
|
-
pass.setIndexBuffer(mesh.indexBuffer,
|
|
5356
|
+
pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);
|
|
5279
5357
|
pass.drawIndexed(mesh.indexCount);
|
|
5280
5358
|
}
|
|
5281
5359
|
}
|
|
@@ -6116,6 +6194,22 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6116
6194
|
* the texel-for-texel comparison that settled it.
|
|
6117
6195
|
*/
|
|
6118
6196
|
private readonly correctedLightViewProj = new Float32Array(16);
|
|
6197
|
+
/**
|
|
6198
|
+
* What a receiver projects through to read the sun's static layers: the environment's
|
|
6199
|
+
* `lightViewProj`, taken at `bindMeshPass` as WebGL2 takes it there.
|
|
6200
|
+
*
|
|
6201
|
+
* **It was the last shadow pass's matrix**, which is the environment's wherever both layers are
|
|
6202
|
+
* drawn with one and the moving layer's wherever they are not: the static layer was then read
|
|
6203
|
+
* through the moving layer's tight square, and a distant pillar lost its shadow on WebGPU and kept
|
|
6204
|
+
* it on WebGL2, measured on demo/dev/sunLayers.html?moving=tight.
|
|
6205
|
+
*/
|
|
6206
|
+
private readonly receiverLightViewProj = new Float32Array(16);
|
|
6207
|
+
/**
|
|
6208
|
+
* The matrix the sun's dynamic shadow pass was last drawn with, uncorrected as the lit stage
|
|
6209
|
+
* projects with, and whether one has been. See `movingLightFor`.
|
|
6210
|
+
*/
|
|
6211
|
+
private readonly movingLightViewProj = new Float32Array(16);
|
|
6212
|
+
private movingLightKnown = false;
|
|
6119
6213
|
/**
|
|
6120
6214
|
* The same matrix *uncorrected*, which is what the flat shader must project with.
|
|
6121
6215
|
*
|
|
@@ -6246,6 +6340,59 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6246
6340
|
this.enableLit('GLASS_SHADOWS');
|
|
6247
6341
|
}
|
|
6248
6342
|
|
|
6343
|
+
/**
|
|
6344
|
+
* A material's layers where they can be drawn: none asked, or a model's map in the mask's place,
|
|
6345
|
+
* said once, leave one layer.
|
|
6346
|
+
*/
|
|
6347
|
+
private layersOf(
|
|
6348
|
+
material: SurfaceMaterial<GpuSurfaceTexture> | null,
|
|
6349
|
+
): SurfaceLayers<GpuSurfaceTexture> | null {
|
|
6350
|
+
const layers = material?.layers ?? null;
|
|
6351
|
+
if (layers === null || layers.repeats.length === 0) return null;
|
|
6352
|
+
if (layersFitBeside(layers, material?.modelMap)) return layers;
|
|
6353
|
+
if (!this.warnedLayersModelMap) {
|
|
6354
|
+
this.warnedLayersModelMap = true;
|
|
6355
|
+
console.warn(
|
|
6356
|
+
"WebGPU: a material's layers are drawn as one: its mask is a map, read where a model's map " +
|
|
6357
|
+
"goes, and this material carries a `modelMap` too. A mask in the ORM array (`mask: 'orm'`) " +
|
|
6358
|
+
"or the vertex colour (`mask: 'vertex'`) leaves the slot free.",
|
|
6359
|
+
);
|
|
6360
|
+
}
|
|
6361
|
+
return null;
|
|
6362
|
+
}
|
|
6363
|
+
|
|
6364
|
+
/** Says once that a layered material's triplanar projection is laid on the horizontal plane. */
|
|
6365
|
+
private warnLayersTriplanar(): void {
|
|
6366
|
+
if (this.warnedLayersTriplanar) return;
|
|
6367
|
+
this.warnedLayersTriplanar = true;
|
|
6368
|
+
console.warn(
|
|
6369
|
+
"WebGPU: a layered material's triplanar projection is laid on the horizontal plane: every " +
|
|
6370
|
+
"layer's maps on three planes would be three times the reads. See `SurfaceMaterial.layers`.",
|
|
6371
|
+
);
|
|
6372
|
+
}
|
|
6373
|
+
|
|
6374
|
+
/**
|
|
6375
|
+
* What the sun's moving layer is read through: the matrix its shadow pass was last drawn with,
|
|
6376
|
+
* where the lit pipelines carry one (`MOVING_SUN`); the environment's otherwise, which every caller
|
|
6377
|
+
* drawing both layers with one matrix hands it anyway.
|
|
6378
|
+
*/
|
|
6379
|
+
private movingLightFor(env: Environment): ReadonlyMat4 {
|
|
6380
|
+
return this.movingLightKnown && this.pipelines.litSwitches.MOVING_SUN
|
|
6381
|
+
? this.movingLightViewProj
|
|
6382
|
+
: env.lightViewProj;
|
|
6383
|
+
}
|
|
6384
|
+
|
|
6385
|
+
/** The moving layer's own matrix on, the first time a frame draws that layer with one. */
|
|
6386
|
+
private enableMovingSun(env: Environment): void {
|
|
6387
|
+
if (this.pipelines.litSwitches.MOVING_SUN || !this.movingLightKnown) return;
|
|
6388
|
+
for (let k = 0; k < 16; k++) {
|
|
6389
|
+
if (this.movingLightViewProj[k] !== env.lightViewProj[k]) {
|
|
6390
|
+
this.enableLit('MOVING_SUN');
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
|
|
6249
6396
|
/** One of the lit stage's switches on, in every cache a lit pipeline can come from. */
|
|
6250
6397
|
private enableLit(feature: LitSwitch): void {
|
|
6251
6398
|
if (this.pipelines.litSwitches[feature]) return;
|
|
@@ -6311,7 +6458,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6311
6458
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
6312
6459
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index] as GPUBuffer);
|
|
6313
6460
|
}
|
|
6314
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
6461
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
6315
6462
|
pass.drawIndexed(geometry.indexCount);
|
|
6316
6463
|
},
|
|
6317
6464
|
/**
|
|
@@ -6401,7 +6548,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6401
6548
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index] as GPUBuffer);
|
|
6402
6549
|
}
|
|
6403
6550
|
pass.setVertexBuffer(index, gpuBatch.buffer);
|
|
6404
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
6551
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
6405
6552
|
pass.drawIndexed(geometry.indexCount, count);
|
|
6406
6553
|
},
|
|
6407
6554
|
skinnedMesh: (mesh, model, palette, material, cloth) => {
|
|
@@ -6463,7 +6610,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6463
6610
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
6464
6611
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index] as GPUBuffer);
|
|
6465
6612
|
}
|
|
6466
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
6613
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
6467
6614
|
pass.drawIndexed(geometry.indexCount);
|
|
6468
6615
|
},
|
|
6469
6616
|
/**
|
|
@@ -6886,6 +7033,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6886
7033
|
this.overlayPipelines === this.pipelines
|
|
6887
7034
|
? [this.pipelines]
|
|
6888
7035
|
: [this.pipelines, this.overlayPipelines];
|
|
7036
|
+
/* A lit draw first needed inside a frame waits for its compile or is left out while it runs. */
|
|
7037
|
+
this.skipCompiling(quality.pipelineCompile === 'skip');
|
|
6889
7038
|
this.variant = flatVariant({
|
|
6890
7039
|
directionalShadows: quality.directionalShadows,
|
|
6891
7040
|
/* No `bakeReflectionProbe` here; see the field. */
|
|
@@ -8138,6 +8287,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8138
8287
|
present['layers'] = key.includes(':layers');
|
|
8139
8288
|
/* Nor is a dynamic mesh's split, which moves positions and normals into buffers of their own. */
|
|
8140
8289
|
present['dynamic'] = key.includes(':dynamic');
|
|
8290
|
+
/* Nor how each attribute travels, which moves every field after a packed one. */
|
|
8291
|
+
markPackingOfKey(present, key);
|
|
8141
8292
|
return present;
|
|
8142
8293
|
}
|
|
8143
8294
|
|
|
@@ -8447,6 +8598,13 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8447
8598
|
present['dynamic'] = true;
|
|
8448
8599
|
key += ':dynamic';
|
|
8449
8600
|
}
|
|
8601
|
+
/*
|
|
8602
|
+
* And how each attribute travels (`vertexPacking.ts`): a field read as sixteen bits rather than
|
|
8603
|
+
* thirty-two, or not interleaved at all, moves every field after it, so the key says so.
|
|
8604
|
+
*/
|
|
8605
|
+
const packing = vertexPackingOf(data, dynamic);
|
|
8606
|
+
markPacking(present, packing);
|
|
8607
|
+
key += packingKey(packing);
|
|
8450
8608
|
const fullKey = `${this.variant}|${key}`;
|
|
8451
8609
|
/*
|
|
8452
8610
|
* **For every target this mesh can land on, which is two when the overlay is its own.**
|
|
@@ -8691,6 +8849,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8691
8849
|
*/
|
|
8692
8850
|
bindMeshPass(camera: Camera, env: Environment): void {
|
|
8693
8851
|
if (this.surface.lost) return;
|
|
8852
|
+
/* The matrix every receiver projects through: the environment's, as WebGL2's. See the field. */
|
|
8853
|
+
this.receiverLightViewProj.set(env.lightViewProj as Float32Array);
|
|
8694
8854
|
|
|
8695
8855
|
/* Rectangles past the fixed arm's are the froxel table's, and without it nobody shades them. */
|
|
8696
8856
|
if (
|
|
@@ -8809,6 +8969,13 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8809
8969
|
this.surfaceFog = FOG_RECEDE;
|
|
8810
8970
|
/* Every material switch off — albedo, ORM map, emissive map, two-sided — as WebGL2 resets them. */
|
|
8811
8971
|
i.fill(0, at('uMaterialFlags'), at('uMaterialFlags') + 4);
|
|
8972
|
+
/* And no projection: maps follow the mesh's coordinates until a material asks otherwise. */
|
|
8973
|
+
f.fill(0, at('uWorldUv'), at('uWorldUv') + 4);
|
|
8974
|
+
/* Nor light through from behind. See `SurfaceMaterial.diffuseTransmission`. */
|
|
8975
|
+
resolveTransmission(null, this.transmission);
|
|
8976
|
+
f.set(this.transmission, at('uGlassTint'));
|
|
8977
|
+
/* Nor layers: one layer until a material asks for more. */
|
|
8978
|
+
f.fill(0, at('uLayers'), at('uLayers') + LAYER_FLOATS);
|
|
8812
8979
|
f[at('uCutout')] = 0;
|
|
8813
8980
|
f[at('uCutout') + 1] = 0;
|
|
8814
8981
|
this.cutoutResolveStaged = 'hard';
|
|
@@ -8891,6 +9058,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8891
9058
|
/* No weighting and no dither until a pass or a crossfade asks. See `setDitherFade`. */
|
|
8892
9059
|
f[at('uWriteMode')] = 0;
|
|
8893
9060
|
f[at('uWriteMode') + 1] = 0;
|
|
9061
|
+
f[at('uWriteMode') + 2] = 0;
|
|
8894
9062
|
/* 1, not 0: a multiplier's identity is one. See `setEnvironmentGain`. */
|
|
8895
9063
|
f[at('uEnvironmentDials') + 2] = 1;
|
|
8896
9064
|
/* No overlay until a draw sets one. See `setSurfaceOverlay`. */
|
|
@@ -9184,6 +9352,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9184
9352
|
f[at('uShadowStrength')] = env.shadowStrength;
|
|
9185
9353
|
f[at('uShadowMapSize')] = this.quality.directionalShadowMapSize;
|
|
9186
9354
|
f[at('uShadowDepthSpan')] = env.shadowDepthSpan;
|
|
9355
|
+
/* The moving layer's own matrix and span, where the lit stage reads one: `movingLightFor`. */
|
|
9356
|
+
this.enableMovingSun(env);
|
|
9357
|
+
const moving = this.movingLightFor(env);
|
|
9358
|
+
f.set(moving as Float32Array, at('uMovingLightViewProj'));
|
|
9359
|
+
f[at('uMovingShadowDepthSpan')] = orthographicDepthSpan(moving);
|
|
9187
9360
|
f[at('uShadowMaxDistance')] = this.quality.directionalShadowMaxDistance;
|
|
9188
9361
|
f[at('uShadowMaxSlope')] = this.quality.directionalShadowMaxSlope;
|
|
9189
9362
|
/* Sampled only where a pass filled it — see `peelFilled` for why existing is not enough. */
|
|
@@ -9386,7 +9559,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9386
9559
|
/** A view block: the camera, the light's matrix and the frame's wind, into `ring` at `slot`. */
|
|
9387
9560
|
private writeView(ring: UniformRing, slot: number, viewProj: Float32Array): void {
|
|
9388
9561
|
ring.writeFloats(slot, FLAT_VIEW_FIELDS.uViewProj.offset, viewProj);
|
|
9389
|
-
ring.writeFloats(slot, FLAT_VIEW_FIELDS.uLightViewProj.offset, this.
|
|
9562
|
+
ring.writeFloats(slot, FLAT_VIEW_FIELDS.uLightViewProj.offset, this.receiverLightViewProj);
|
|
9390
9563
|
/* The scene's clock, which a bone animation's instances read their moment from. */
|
|
9391
9564
|
ring.writeFloat(slot, FLAT_VIEW_FIELDS.uSceneTime.offset, this.animationTime);
|
|
9392
9565
|
const d = this.frameWind;
|
|
@@ -9595,6 +9768,225 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9595
9768
|
);
|
|
9596
9769
|
}
|
|
9597
9770
|
|
|
9771
|
+
/**
|
|
9772
|
+
* Compile off the main thread every pipeline a draw of `mesh` in `material` takes. **The other
|
|
9773
|
+
* backend's `prepareMesh` carries the contract**; here a draw's key is `meshShape`'s, so a prepared
|
|
9774
|
+
* key is exactly the one the draw asks for, and the compiles are started as a skipping draw starts
|
|
9775
|
+
* them (`PipelineCache.lit`) whatever `pipelineCompile` says, into the frame's own caches.
|
|
9776
|
+
*/
|
|
9777
|
+
prepareMesh(
|
|
9778
|
+
mesh: GpuMesh,
|
|
9779
|
+
material: SurfaceMaterial<GpuSurfaceTexture> | null,
|
|
9780
|
+
options: PrepareOptions = {},
|
|
9781
|
+
): void {
|
|
9782
|
+
if (this.surface.lost) return;
|
|
9783
|
+
this.preparing(
|
|
9784
|
+
material,
|
|
9785
|
+
(into, blend) => {
|
|
9786
|
+
const skinned = mesh.isSkinned;
|
|
9787
|
+
const eight = skinned && mesh.isSkinnedEight;
|
|
9788
|
+
for (const morphed of mesh.morph === null ? NOT_MORPHED : MORPHED_OR_NOT) {
|
|
9789
|
+
this.meshShape(mesh, blend, PREPARED_DRAW, skinned, morphed, eight, false);
|
|
9790
|
+
this.meshPipeline(blend, false, skinned, morphed, eight, false, into);
|
|
9791
|
+
}
|
|
9792
|
+
},
|
|
9793
|
+
options,
|
|
9794
|
+
);
|
|
9795
|
+
}
|
|
9796
|
+
|
|
9797
|
+
/** `prepareMesh` for an instanced batch. See the other backend's `prepareInstanced`. */
|
|
9798
|
+
prepareInstanced(
|
|
9799
|
+
batch: GpuInstancedBatch,
|
|
9800
|
+
material: SurfaceMaterial<GpuSurfaceTexture> | null,
|
|
9801
|
+
options: PrepareOptions = {},
|
|
9802
|
+
): void {
|
|
9803
|
+
if (this.surface.lost) return;
|
|
9804
|
+
this.preparing(
|
|
9805
|
+
material,
|
|
9806
|
+
(into, blend) => {
|
|
9807
|
+
const coverage = !blend && this.cutoutResolveStaged === 'coverage';
|
|
9808
|
+
this.instancedPipeline(
|
|
9809
|
+
batch.mesh,
|
|
9810
|
+
batch.animation !== null,
|
|
9811
|
+
blend,
|
|
9812
|
+
false,
|
|
9813
|
+
PREPARED_DRAW,
|
|
9814
|
+
true,
|
|
9815
|
+
0,
|
|
9816
|
+
coverage,
|
|
9817
|
+
into,
|
|
9818
|
+
);
|
|
9819
|
+
},
|
|
9820
|
+
options,
|
|
9821
|
+
);
|
|
9822
|
+
}
|
|
9823
|
+
|
|
9824
|
+
/**
|
|
9825
|
+
* `material` set, every cache a draw in it may build into passed to `start` with whether that draw
|
|
9826
|
+
* blends, compiles started rather than built (`PipelineCache.lit`), and then no material set and
|
|
9827
|
+
* each cache back to what `pipelineCompile` says.
|
|
9828
|
+
*/
|
|
9829
|
+
private preparing(
|
|
9830
|
+
material: SurfaceMaterial<GpuSurfaceTexture> | null,
|
|
9831
|
+
start: (into: PipelineCache, blend: boolean) => void,
|
|
9832
|
+
options: PrepareOptions,
|
|
9833
|
+
): void {
|
|
9834
|
+
this.setMaterial(material);
|
|
9835
|
+
this.skipCompiling(true);
|
|
9836
|
+
try {
|
|
9837
|
+
start(this.pipelines, false);
|
|
9838
|
+
if (options.translucent === true) {
|
|
9839
|
+
start(this.pipelines, true);
|
|
9840
|
+
/* A blended draw in a reconstructing frame lands in the late pass, whose cache is its own. */
|
|
9841
|
+
if (this.latePipelines !== this.pipelines) start(this.latePipelines, true);
|
|
9842
|
+
}
|
|
9843
|
+
} finally {
|
|
9844
|
+
this.skipCompiling(this.quality.pipelineCompile === 'skip');
|
|
9845
|
+
this.setMaterial(null);
|
|
9846
|
+
}
|
|
9847
|
+
}
|
|
9848
|
+
|
|
9849
|
+
/** What `meshShape` worked out for the mesh draw at hand. Reused, so a draw allocates no record. */
|
|
9850
|
+
private readonly drawShape = {
|
|
9851
|
+
base: '',
|
|
9852
|
+
key: '',
|
|
9853
|
+
depthWrite: true,
|
|
9854
|
+
layer: 0,
|
|
9855
|
+
adds: false,
|
|
9856
|
+
coverage: false,
|
|
9857
|
+
splits: false,
|
|
9858
|
+
modelKind: null as SurfaceModelKind | null,
|
|
9859
|
+
};
|
|
9860
|
+
|
|
9861
|
+
/**
|
|
9862
|
+
* The key a mesh draw's pipeline is cached under, and the pipeline state that key encodes, under
|
|
9863
|
+
* the current material and these options, into `drawShape`. **The one statement of it**:
|
|
9864
|
+
* `submitMesh` draws by it and `prepareMesh` compiles by it, so the two cannot disagree about a key.
|
|
9865
|
+
*/
|
|
9866
|
+
private meshShape(
|
|
9867
|
+
mesh: GpuMesh,
|
|
9868
|
+
blend: boolean,
|
|
9869
|
+
options: TranslucentMeshOptions,
|
|
9870
|
+
skinned: boolean,
|
|
9871
|
+
morphed: boolean,
|
|
9872
|
+
eight: boolean,
|
|
9873
|
+
clothed: boolean,
|
|
9874
|
+
): WebGPURenderer['drawShape'] {
|
|
9875
|
+
const modelKind = this.materialModel?.kind ?? null;
|
|
9876
|
+
const base = (mesh as GpuMesh & { key?: string }).key ?? 'flat:s0:u0';
|
|
9877
|
+
const keyed =
|
|
9878
|
+
`${base}${morphed ? '|morph' : ''}${skinned ? (eight ? '|skin8' : '|skin') : ''}` +
|
|
9879
|
+
`${clothed ? '|cloth' : ''}`;
|
|
9880
|
+
/*
|
|
9881
|
+
* Depth writing and the overlay layer are pipeline state on this backend, so they belong in
|
|
9882
|
+
* the key: two draws of one mesh differing only in these would otherwise share a cached
|
|
9883
|
+
* pipeline and the second would silently take the first one's depth behaviour.
|
|
9884
|
+
*
|
|
9885
|
+
* **Both suffixes are empty for the defaults**, which is what keeps the keys `createMesh`
|
|
9886
|
+
* warms — `fullKey` and `${'`'}${'$'}{fullKey}|blend${'`'}` — the ones an ordinary draw still asks for. A
|
|
9887
|
+
* caller that opts in pays one compile on its first draw, the same bargain the overlay
|
|
9888
|
+
* pipelines above already make.
|
|
9889
|
+
*/
|
|
9890
|
+
const depthWrite = options.depthWrite ?? true;
|
|
9891
|
+
const layer = Math.min(Math.max(Math.round(options.depthLayer ?? 0), 0), MAX_DEPTH_LAYER);
|
|
9892
|
+
/* Added rather than blended over: its own blend state, so its own pipeline. */
|
|
9893
|
+
const adds = blend && options.additive === true;
|
|
9894
|
+
const coverage = !blend && this.cutoutResolveStaged === 'coverage';
|
|
9895
|
+
/* Skin under the screen-space blur draws its two halves; see `keepSkinDiffuse`. */
|
|
9896
|
+
const splits = modelKind === 'skin' && !blend && this.skinSplits();
|
|
9897
|
+
const key =
|
|
9898
|
+
`${keyed}${blend ? (adds ? '|add' : '|blend') : ''}` +
|
|
9899
|
+
`${depthWrite ? '' : '|nodepth'}${layer > 0 ? `|dl${layer}` : ''}` +
|
|
9900
|
+
/* The two order-independent buffers are two formats and two blend states, so a draw into
|
|
9901
|
+
one must never share a cached pipeline with the same mesh drawn into the other. */
|
|
9902
|
+
`${this.oitMode === 'none' ? '' : `|oit${this.oitMode}`}` +
|
|
9903
|
+
/* Culling is pipeline state here: a two-sided surface culls nothing. */
|
|
9904
|
+
`${this.materialDoubleSided ? '|2s' : ''}` +
|
|
9905
|
+
/* And so is alpha-to-coverage, for a dithered cutout in a multisampled frame. */
|
|
9906
|
+
`${coverage ? '|a2c' : ''}` +
|
|
9907
|
+
/* And the material's shading model, which is the fragment stage's own switch. */
|
|
9908
|
+
`${modelKind === null ? '' : `|m:${modelKind}`}` +
|
|
9909
|
+
/* And which half of a skin it draws, under the screen-space blur. */
|
|
9910
|
+
`${splits ? '|ss' : ''}`;
|
|
9911
|
+
|
|
9912
|
+
const shape = this.drawShape;
|
|
9913
|
+
shape.base = base;
|
|
9914
|
+
shape.key = key;
|
|
9915
|
+
shape.depthWrite = depthWrite;
|
|
9916
|
+
shape.layer = layer;
|
|
9917
|
+
shape.adds = adds;
|
|
9918
|
+
shape.coverage = coverage;
|
|
9919
|
+
shape.splits = splits;
|
|
9920
|
+
shape.modelKind = modelKind;
|
|
9921
|
+
return shape;
|
|
9922
|
+
}
|
|
9923
|
+
|
|
9924
|
+
/**
|
|
9925
|
+
* The pipeline `drawShape` names: the one built, one built here, or none where draws skip a
|
|
9926
|
+
* compile and this one has not landed (`PipelineCache.lit`) — and none for a split skin until all
|
|
9927
|
+
* of its halves have.
|
|
9928
|
+
*/
|
|
9929
|
+
private meshPipeline(
|
|
9930
|
+
blend: boolean,
|
|
9931
|
+
lands: boolean,
|
|
9932
|
+
skinned: boolean,
|
|
9933
|
+
morphed: boolean,
|
|
9934
|
+
eight: boolean,
|
|
9935
|
+
clothed: boolean,
|
|
9936
|
+
/** Which cache to build into: the one the draw's target reads, unless a preparation says. */
|
|
9937
|
+
into: PipelineCache | null = null,
|
|
9938
|
+
): GPURenderPipeline | null {
|
|
9939
|
+
const { base, key, depthWrite, layer, adds, coverage, splits, modelKind } = this.drawShape;
|
|
9940
|
+
/*
|
|
9941
|
+
* **Asynchronous by default, synchronous if it has to be, and never absent.**
|
|
9942
|
+
*
|
|
9943
|
+
* `createMesh` compiles off the main thread, so a draw can legitimately arrive before the
|
|
9944
|
+
* driver has finished — a consumer that awaits `ready()` never sees this, and one that
|
|
9945
|
+
* draws immediately gets the old behaviour rather than an exception. Correctness cannot be
|
|
9946
|
+
* allowed to depend on which of those happened, so the miss builds the pipeline here and
|
|
9947
|
+
* pays the compile it was trying to avoid. That is a slow frame; the alternative is a
|
|
9948
|
+
* thrown error in the middle of one.
|
|
9949
|
+
*/
|
|
9950
|
+
const pipelines = into ?? (blend ? this.blendedPipelines(lands) : this.targetPipelines());
|
|
9951
|
+
let pipeline: GPURenderPipeline | null = pipelines.peek(key) ?? null;
|
|
9952
|
+
if (pipeline === null) {
|
|
9953
|
+
const present = this.meshPresent.get(base);
|
|
9954
|
+
if (present === undefined) {
|
|
9955
|
+
throw new Error(`WebGPU: no pipeline for ${key}; createMesh builds it`);
|
|
9956
|
+
}
|
|
9957
|
+
pipeline = flatPipeline(
|
|
9958
|
+
pipelines,
|
|
9959
|
+
this.surface.device,
|
|
9960
|
+
this.flatLayoutFor(skinned, morphed),
|
|
9961
|
+
this.variant,
|
|
9962
|
+
key,
|
|
9963
|
+
present,
|
|
9964
|
+
adds ? 'additive' : blend,
|
|
9965
|
+
skinned,
|
|
9966
|
+
morphed,
|
|
9967
|
+
depthWrite,
|
|
9968
|
+
layer,
|
|
9969
|
+
false,
|
|
9970
|
+
this.oitMode,
|
|
9971
|
+
this.materialDoubleSided,
|
|
9972
|
+
coverage,
|
|
9973
|
+
eight,
|
|
9974
|
+
clothed,
|
|
9975
|
+
modelKind,
|
|
9976
|
+
splits ? 'scene' : 'whole',
|
|
9977
|
+
);
|
|
9978
|
+
}
|
|
9979
|
+
/* A split skin draws its three halves or none of them. */
|
|
9980
|
+
if (
|
|
9981
|
+
pipeline !== null &&
|
|
9982
|
+
splits &&
|
|
9983
|
+
!this.skinHalvesReady(key, base, skinned, morphed, layer, eight, clothed)
|
|
9984
|
+
) {
|
|
9985
|
+
pipeline = null;
|
|
9986
|
+
}
|
|
9987
|
+
return pipeline;
|
|
9988
|
+
}
|
|
9989
|
+
|
|
9598
9990
|
/**
|
|
9599
9991
|
* @param blend Which of the two entry points above this came through, and **not** something to
|
|
9600
9992
|
* infer from `opacity`.
|
|
@@ -9720,7 +10112,6 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9720
10112
|
/* An eye's axis is the draw's, written into the open material. See `eyeAxisInWorld`. */
|
|
9721
10113
|
const modelKind = this.materialModel?.kind ?? null;
|
|
9722
10114
|
if (modelKind === 'eye') this.writeEyeAxis(model, this.skinPaletteSlot >= 0 && mesh.isSkinned);
|
|
9723
|
-
const base = (mesh as GpuMesh & { key?: string }).key ?? 'flat:s0:u0';
|
|
9724
10115
|
/*
|
|
9725
10116
|
* A rigged mesh takes the skinned vertex variant, an unrigged one the plain variant, and the
|
|
9726
10117
|
* key carries the flag so the two never share a cached pipeline. A rigged mesh whose caller
|
|
@@ -9735,41 +10126,6 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9735
10126
|
/* A cloth set and a skinned draw: the pipeline built with CLOTH_BOUND on, warmed when the
|
|
9736
10127
|
binding was made. See `createClothBinding`. */
|
|
9737
10128
|
const clothed = skinned && this.cloth !== null;
|
|
9738
|
-
const keyed =
|
|
9739
|
-
`${base}${morphed ? '|morph' : ''}${skinned ? (eight ? '|skin8' : '|skin') : ''}` +
|
|
9740
|
-
`${clothed ? '|cloth' : ''}`;
|
|
9741
|
-
/*
|
|
9742
|
-
* Depth writing and the overlay layer are pipeline state on this backend, so they belong in
|
|
9743
|
-
* the key: two draws of one mesh differing only in these would otherwise share a cached
|
|
9744
|
-
* pipeline and the second would silently take the first one's depth behaviour.
|
|
9745
|
-
*
|
|
9746
|
-
* **Both suffixes are empty for the defaults**, which is what keeps the keys `createMesh`
|
|
9747
|
-
* warms — `fullKey` and `${'`'}${'$'}{fullKey}|blend${'`'}` — the ones an ordinary draw still asks for. A
|
|
9748
|
-
* caller that opts in pays one compile on its first draw, the same bargain the overlay
|
|
9749
|
-
* pipelines above already make.
|
|
9750
|
-
*/
|
|
9751
|
-
const depthWrite = options.depthWrite ?? true;
|
|
9752
|
-
const layer = Math.min(Math.max(Math.round(options.depthLayer ?? 0), 0), MAX_DEPTH_LAYER);
|
|
9753
|
-
/* Added rather than blended over: its own blend state, so its own pipeline. */
|
|
9754
|
-
const adds = blend && options.additive === true;
|
|
9755
|
-
const coverage = !blend && this.cutoutResolveStaged === 'coverage';
|
|
9756
|
-
/* Skin under the screen-space blur draws its two halves; see `keepSkinDiffuse`. */
|
|
9757
|
-
const splits = modelKind === 'skin' && !blend && this.skinSplits();
|
|
9758
|
-
const key =
|
|
9759
|
-
`${keyed}${blend ? (adds ? '|add' : '|blend') : ''}` +
|
|
9760
|
-
`${depthWrite ? '' : '|nodepth'}${layer > 0 ? `|dl${layer}` : ''}` +
|
|
9761
|
-
/* The two order-independent buffers are two formats and two blend states, so a draw into
|
|
9762
|
-
one must never share a cached pipeline with the same mesh drawn into the other. */
|
|
9763
|
-
`${this.oitMode === 'none' ? '' : `|oit${this.oitMode}`}` +
|
|
9764
|
-
/* Culling is pipeline state here: a two-sided surface culls nothing. */
|
|
9765
|
-
`${this.materialDoubleSided ? '|2s' : ''}` +
|
|
9766
|
-
/* And so is alpha-to-coverage, for a dithered cutout in a multisampled frame. */
|
|
9767
|
-
`${coverage ? '|a2c' : ''}` +
|
|
9768
|
-
/* And the material's shading model, which is the fragment stage's own switch. */
|
|
9769
|
-
`${modelKind === null ? '' : `|m:${modelKind}`}` +
|
|
9770
|
-
/* And which half of a skin it draws, under the screen-space blur. */
|
|
9771
|
-
`${splits ? '|ss' : ''}`;
|
|
9772
|
-
|
|
9773
10129
|
/*
|
|
9774
10130
|
* The morph uniforms, written only for a morphed draw and read from the *morphed* variant's
|
|
9775
10131
|
* field table. `FLAT_VERT_FIELDS` is the plain variant's and does not name them at all — every
|
|
@@ -9782,45 +10138,17 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9782
10138
|
this.perDraw.writeInt(slot, fields.uMorphTargetCount.offset, mesh.morph.targetCount);
|
|
9783
10139
|
this.perDraw.writeInt(slot, fields.uMorphTextureWidth.offset, mesh.morph.width);
|
|
9784
10140
|
}
|
|
9785
|
-
/*
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
let pipeline = pipelines.peek(key);
|
|
9797
|
-
if (pipeline === undefined) {
|
|
9798
|
-
const present = this.meshPresent.get(base);
|
|
9799
|
-
if (present === undefined) {
|
|
9800
|
-
throw new Error(`WebGPU: no pipeline for ${key}; createMesh builds it`);
|
|
9801
|
-
}
|
|
9802
|
-
pipeline = flatPipeline(
|
|
9803
|
-
pipelines,
|
|
9804
|
-
this.surface.device,
|
|
9805
|
-
this.flatLayoutFor(skinned, morphed),
|
|
9806
|
-
this.variant,
|
|
9807
|
-
key,
|
|
9808
|
-
present,
|
|
9809
|
-
adds ? 'additive' : blend,
|
|
9810
|
-
skinned,
|
|
9811
|
-
morphed,
|
|
9812
|
-
depthWrite,
|
|
9813
|
-
layer,
|
|
9814
|
-
false,
|
|
9815
|
-
this.oitMode,
|
|
9816
|
-
this.materialDoubleSided,
|
|
9817
|
-
coverage,
|
|
9818
|
-
eight,
|
|
9819
|
-
clothed,
|
|
9820
|
-
modelKind,
|
|
9821
|
-
splits ? 'scene' : 'whole',
|
|
9822
|
-
);
|
|
9823
|
-
}
|
|
10141
|
+
/* Its pipeline, by the key a prepared draw compiled too: see `meshShape`. */
|
|
10142
|
+
const { base, key, layer, depthWrite, splits } = this.meshShape(
|
|
10143
|
+
mesh,
|
|
10144
|
+
blend,
|
|
10145
|
+
options,
|
|
10146
|
+
skinned,
|
|
10147
|
+
morphed,
|
|
10148
|
+
eight,
|
|
10149
|
+
clothed,
|
|
10150
|
+
);
|
|
10151
|
+
let pipeline = this.meshPipeline(blend, lands, skinned, morphed, eight, clothed);
|
|
9824
10152
|
|
|
9825
10153
|
/*
|
|
9826
10154
|
* Guards the draw call only, not the restore below.
|
|
@@ -9838,8 +10166,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9838
10166
|
* close to the 256-slot budget before the gateway, the play controls, the waveform, the
|
|
9839
10167
|
* label handles and the sparkles are even counted.
|
|
9840
10168
|
*/
|
|
9841
|
-
|
|
9842
|
-
|
|
10169
|
+
/* A draw whose pipeline is still compiling is left out, its state restored all the same. */
|
|
10170
|
+
const material = pipeline === null ? null : this.materialSlotForDraw();
|
|
10171
|
+
if (pipeline !== null && material !== null) {
|
|
9843
10172
|
/*
|
|
9844
10173
|
* Record rather than issue. Every per-draw value is already in `slot` and `material` —
|
|
9845
10174
|
* the uniform rings are the state arena this design would otherwise have had to build —
|
|
@@ -9861,6 +10190,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9861
10190
|
}
|
|
9862
10191
|
command.vertexCount = mesh.vertexBuffers.length;
|
|
9863
10192
|
command.indexBuffer = mesh.indexBuffer;
|
|
10193
|
+
command.indexFormat = mesh.indexFormat;
|
|
9864
10194
|
command.indexed = true;
|
|
9865
10195
|
command.count = mesh.indexCount;
|
|
9866
10196
|
} else {
|
|
@@ -9876,7 +10206,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9876
10206
|
for (let index = 0; index < mesh.vertexBuffers.length; index++) {
|
|
9877
10207
|
pass.setVertexBuffer(index, mesh.vertexBuffers[index] as GPUBuffer);
|
|
9878
10208
|
}
|
|
9879
|
-
pass.setIndexBuffer(mesh.indexBuffer,
|
|
10209
|
+
pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);
|
|
9880
10210
|
pass.drawIndexed(mesh.indexCount);
|
|
9881
10211
|
}
|
|
9882
10212
|
if (splits) {
|
|
@@ -10209,6 +10539,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10209
10539
|
const seeAt = this.materialField('uSeeThrough');
|
|
10210
10540
|
this.perFrameFloats[seeAt] = 0;
|
|
10211
10541
|
this.perFrameFloats[seeAt + 2] = 0;
|
|
10542
|
+
/* The material's light through, over the pane's tint: transmission.ts. */
|
|
10543
|
+
this.perFrameFloats.set(this.transmission, this.materialField('uGlassTint'));
|
|
10212
10544
|
}
|
|
10213
10545
|
|
|
10214
10546
|
/**
|
|
@@ -10223,6 +10555,76 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10223
10555
|
* **What it gives up** against `drawMesh` is skinning and morphing, refused where the variant
|
|
10224
10556
|
* is built, and a per-instance opacity: `opacity` here dims the whole batch.
|
|
10225
10557
|
*/
|
|
10558
|
+
/**
|
|
10559
|
+
* An instanced draw's pipeline under the current material and these options: the one built, one
|
|
10560
|
+
* built here, or none while it compiles (`PipelineCache.lit`), its key, base and model left in
|
|
10561
|
+
* `drawShape`. **The one statement of an instanced key**: `submitInstanced` draws by it and
|
|
10562
|
+
* `prepareInstanced` compiles by it.
|
|
10563
|
+
*/
|
|
10564
|
+
private instancedPipeline(
|
|
10565
|
+
mesh: GpuMesh,
|
|
10566
|
+
animated: boolean,
|
|
10567
|
+
blend: boolean,
|
|
10568
|
+
lands: boolean,
|
|
10569
|
+
options: TranslucentMeshOptions,
|
|
10570
|
+
depthWrite: boolean,
|
|
10571
|
+
layer: number,
|
|
10572
|
+
coverage: boolean,
|
|
10573
|
+
/** Which cache to build into: the one the draw's target reads, unless a preparation says. */
|
|
10574
|
+
into: PipelineCache | null = null,
|
|
10575
|
+
): GPURenderPipeline | null {
|
|
10576
|
+
/* A batch shades by its material's model too; an eye's axis is a placement's, not a batch's. */
|
|
10577
|
+
const modelKind = this.materialModel?.kind ?? null;
|
|
10578
|
+
const base = (mesh as GpuMesh & { key?: string }).key ?? 'flat:s0:u0';
|
|
10579
|
+
/* `|inst` in the key, for the reason every other suffix is there: an instanced pipeline binds
|
|
10580
|
+
a third vertex buffer and a different vertex module, and sharing a cache entry with the
|
|
10581
|
+
plain one would hand a draw the wrong layout. */
|
|
10582
|
+
const adds = blend && options.additive === true;
|
|
10583
|
+
const key =
|
|
10584
|
+
`${base}|inst${!animated ? '' : '|anim'}${blend ? (adds ? '|add' : '|blend') : ''}` +
|
|
10585
|
+
`${depthWrite ? '' : '|nodepth'}${layer > 0 ? `|dl${layer}` : ''}` +
|
|
10586
|
+
`${this.materialDoubleSided ? '|2s' : ''}${coverage ? '|a2c' : ''}` +
|
|
10587
|
+
`${modelKind === null ? '' : `|m:${modelKind}`}`;
|
|
10588
|
+
|
|
10589
|
+
const shape = this.drawShape;
|
|
10590
|
+
shape.base = base;
|
|
10591
|
+
shape.key = key;
|
|
10592
|
+
shape.modelKind = modelKind;
|
|
10593
|
+
const pipelines = into ?? (blend ? this.blendedPipelines(lands) : this.targetPipelines());
|
|
10594
|
+
let pipeline: GPURenderPipeline | null = pipelines.peek(key) ?? null;
|
|
10595
|
+
if (pipeline === null) {
|
|
10596
|
+
const present = this.meshPresent.get(base);
|
|
10597
|
+
if (present === undefined) {
|
|
10598
|
+
throw new Error(`WebGPU: no mesh attributes recorded for ${base}; createMesh records them`);
|
|
10599
|
+
}
|
|
10600
|
+
pipeline = flatPipeline(
|
|
10601
|
+
pipelines,
|
|
10602
|
+
this.surface.device,
|
|
10603
|
+
!animated ? this.flatLayoutFor(false, false) : this.animatedBindGroupLayout,
|
|
10604
|
+
this.variant,
|
|
10605
|
+
key,
|
|
10606
|
+
present,
|
|
10607
|
+
adds ? 'additive' : blend,
|
|
10608
|
+
false,
|
|
10609
|
+
false,
|
|
10610
|
+
depthWrite,
|
|
10611
|
+
layer,
|
|
10612
|
+
true,
|
|
10613
|
+
'none',
|
|
10614
|
+
this.materialDoubleSided,
|
|
10615
|
+
coverage,
|
|
10616
|
+
false,
|
|
10617
|
+
false,
|
|
10618
|
+
modelKind,
|
|
10619
|
+
'whole',
|
|
10620
|
+
false,
|
|
10621
|
+
animated,
|
|
10622
|
+
);
|
|
10623
|
+
}
|
|
10624
|
+
|
|
10625
|
+
return pipeline;
|
|
10626
|
+
}
|
|
10627
|
+
|
|
10226
10628
|
/**
|
|
10227
10629
|
* The next of a batch's cull slots this frame, with its cull queued against the current view; null
|
|
10228
10630
|
* when the batch has drawn from `MAX_CULL_SLOTS` views already or the frame's job ring is full,
|
|
@@ -10341,53 +10743,22 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10341
10743
|
const own =
|
|
10342
10744
|
ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog) || ditheredBlend;
|
|
10343
10745
|
if (own) this.materials.dirty();
|
|
10344
|
-
/*
|
|
10345
|
-
const
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
const pipelines = blend ? this.blendedPipelines(lands) : this.targetPipelines();
|
|
10358
|
-
let pipeline = pipelines.peek(key);
|
|
10359
|
-
if (pipeline === undefined) {
|
|
10360
|
-
const present = this.meshPresent.get(base);
|
|
10361
|
-
if (present === undefined) {
|
|
10362
|
-
throw new Error(`WebGPU: no mesh attributes recorded for ${base}; createMesh records them`);
|
|
10363
|
-
}
|
|
10364
|
-
pipeline = flatPipeline(
|
|
10365
|
-
pipelines,
|
|
10366
|
-
this.surface.device,
|
|
10367
|
-
animation === null ? this.flatLayoutFor(false, false) : this.animatedBindGroupLayout,
|
|
10368
|
-
this.variant,
|
|
10369
|
-
key,
|
|
10370
|
-
present,
|
|
10371
|
-
adds ? 'additive' : blend,
|
|
10372
|
-
false,
|
|
10373
|
-
false,
|
|
10374
|
-
depthWrite,
|
|
10375
|
-
layer,
|
|
10376
|
-
true,
|
|
10377
|
-
'none',
|
|
10378
|
-
this.materialDoubleSided,
|
|
10379
|
-
coverage,
|
|
10380
|
-
false,
|
|
10381
|
-
false,
|
|
10382
|
-
modelKind,
|
|
10383
|
-
'whole',
|
|
10384
|
-
false,
|
|
10385
|
-
animation !== null,
|
|
10386
|
-
);
|
|
10387
|
-
}
|
|
10746
|
+
/* Its pipeline, by the key a prepared batch compiled too: see `instancedPipeline`. */
|
|
10747
|
+
const pipeline = this.instancedPipeline(
|
|
10748
|
+
mesh,
|
|
10749
|
+
animation !== null,
|
|
10750
|
+
blend,
|
|
10751
|
+
lands,
|
|
10752
|
+
options,
|
|
10753
|
+
depthWrite,
|
|
10754
|
+
layer,
|
|
10755
|
+
coverage,
|
|
10756
|
+
);
|
|
10757
|
+
const { base, key, modelKind } = this.drawShape;
|
|
10388
10758
|
|
|
10389
|
-
|
|
10390
|
-
|
|
10759
|
+
/* A batch whose pipeline is still compiling is left out, as a mesh is. */
|
|
10760
|
+
const material = pipeline === null ? null : this.materialSlotForDraw();
|
|
10761
|
+
if (pipeline !== null && material !== null) {
|
|
10391
10762
|
const command = blend
|
|
10392
10763
|
? this.recordBlended(0, lands)
|
|
10393
10764
|
: this.recordDraw(0, this.currentTarget());
|
|
@@ -10409,6 +10780,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10409
10780
|
command.vertexBuffers[index] = instanceBuffer;
|
|
10410
10781
|
command.vertexCount = mesh.vertexBuffers.length + 1;
|
|
10411
10782
|
command.indexBuffer = mesh.indexBuffer;
|
|
10783
|
+
command.indexFormat = mesh.indexFormat;
|
|
10412
10784
|
command.indexed = true;
|
|
10413
10785
|
command.count = mesh.indexCount;
|
|
10414
10786
|
command.instances = count;
|
|
@@ -10429,7 +10801,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10429
10801
|
pass.setVertexBuffer(index, mesh.vertexBuffers[index] as GPUBuffer);
|
|
10430
10802
|
}
|
|
10431
10803
|
pass.setVertexBuffer(index, instanceBuffer);
|
|
10432
|
-
pass.setIndexBuffer(mesh.indexBuffer,
|
|
10804
|
+
pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);
|
|
10433
10805
|
if (cut === null) pass.drawIndexed(mesh.indexCount, count);
|
|
10434
10806
|
else pass.drawIndexedIndirect(cut.args, 0);
|
|
10435
10807
|
}
|
|
@@ -10664,6 +11036,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10664
11036
|
if (view === null) return false;
|
|
10665
11037
|
|
|
10666
11038
|
this.lightViewProj.set(lightViewProj as Float32Array);
|
|
11039
|
+
/* The moving layer is read through the matrix it is drawn with: see `movingLightFor`. */
|
|
11040
|
+
if (dynamic) {
|
|
11041
|
+
this.movingLightViewProj.set(lightViewProj as Float32Array);
|
|
11042
|
+
this.movingLightKnown = true;
|
|
11043
|
+
}
|
|
10667
11044
|
this.reopenViews();
|
|
10668
11045
|
mat4.multiply(this.correctedLightViewProj, SHADOW_CLIP_CORRECTION, lightViewProj);
|
|
10669
11046
|
this.shadowLayerIsPeel = peel;
|
|
@@ -10975,10 +11352,13 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10975
11352
|
this.lateViewBlock.slot = list.viewOffset(0);
|
|
10976
11353
|
this.materials.dirty();
|
|
10977
11354
|
this.staticRecording = recording;
|
|
11355
|
+
/* A recorded list keeps what it recorded, so every draw of it waits for its pipeline. */
|
|
11356
|
+
this.skipCompiling(false);
|
|
10978
11357
|
try {
|
|
10979
11358
|
this.sceneCasterMaterial = undefined;
|
|
10980
11359
|
replayStaticDraws(list, this.sceneCasterSink);
|
|
10981
11360
|
} finally {
|
|
11361
|
+
this.skipCompiling(this.quality.pipelineCompile === 'skip');
|
|
10982
11362
|
this.staticRecording = null;
|
|
10983
11363
|
this.perDraw = perDraw;
|
|
10984
11364
|
this.passRing = passRing;
|
|
@@ -11285,7 +11665,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11285
11665
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
11286
11666
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index] as GPUBuffer);
|
|
11287
11667
|
}
|
|
11288
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
11668
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
11289
11669
|
pass.drawIndexed(geometry.indexCount);
|
|
11290
11670
|
},
|
|
11291
11671
|
instanced: (batch, data, material, glass) => {
|
|
@@ -11330,7 +11710,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11330
11710
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index] as GPUBuffer);
|
|
11331
11711
|
}
|
|
11332
11712
|
pass.setVertexBuffer(index, gpuBatch.buffer);
|
|
11333
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
11713
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
11334
11714
|
pass.drawIndexed(geometry.indexCount, count);
|
|
11335
11715
|
},
|
|
11336
11716
|
skinnedMesh: (mesh, model, palette, _material, glass) => {
|
|
@@ -11366,7 +11746,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11366
11746
|
for (let index = 0; index < geometry.vertexBuffers.length; index++) {
|
|
11367
11747
|
pass.setVertexBuffer(index, geometry.vertexBuffers[index] as GPUBuffer);
|
|
11368
11748
|
}
|
|
11369
|
-
pass.setIndexBuffer(geometry.indexBuffer,
|
|
11749
|
+
pass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);
|
|
11370
11750
|
pass.drawIndexed(geometry.indexCount);
|
|
11371
11751
|
},
|
|
11372
11752
|
};
|
|
@@ -13865,6 +14245,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13865
14245
|
/* Uncorrected, for the reason the shaft's own copy of this line carries: the shader does its
|
|
13866
14246
|
own `* 0.5 + 0.5`, and correcting the matrix as well doubles it. */
|
|
13867
14247
|
f.set(env?.lightViewProj ?? this.mediumIdentity, at('uLightViewProj'));
|
|
14248
|
+
f.set(
|
|
14249
|
+
(env === null ? this.mediumIdentity : this.movingLightFor(env)) as Float32Array,
|
|
14250
|
+
at('uMovingLightViewProj'),
|
|
14251
|
+
);
|
|
13868
14252
|
ints[at('uPeeledShadowEnabled')] = this.peelView === null || !this.peelFilled ? 0 : 1;
|
|
13869
14253
|
device.queue.writeBuffer(this.mediumUniforms, 0, this.mediumStaging);
|
|
13870
14254
|
|
|
@@ -13945,6 +14329,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13945
14329
|
const f = this.aoFloats;
|
|
13946
14330
|
const at = (name: string): number => this.postField(AO_FRAG_FIELDS, name);
|
|
13947
14331
|
f[at('uRadius')] = this.quality.ambientOcclusionRadius;
|
|
14332
|
+
f.set(this.aoFade, at('uFade'));
|
|
13948
14333
|
f.set(this.aoProjScale, at('uProjScale'));
|
|
13949
14334
|
f.set(this.aoInvProjection, at('uInvProjection'));
|
|
13950
14335
|
device.queue.writeBuffer(this.aoUniforms, 0, this.aoStaging);
|
|
@@ -14389,6 +14774,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14389
14774
|
true,
|
|
14390
14775
|
animation !== null,
|
|
14391
14776
|
);
|
|
14777
|
+
/* Still compiling where draws skip: the probe stands in for this surface until it lands. */
|
|
14778
|
+
if (pipeline === null) return;
|
|
14392
14779
|
/* A static list's surface halves go into its own pool, beside its draws. */
|
|
14393
14780
|
const command =
|
|
14394
14781
|
this.staticRecording === null ? surface.take() : takeRecorded(this.staticRecording.surface);
|
|
@@ -14409,6 +14796,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14409
14796
|
if (instanceBuffer !== null) command.vertexBuffers[index++] = instanceBuffer;
|
|
14410
14797
|
command.vertexCount = index;
|
|
14411
14798
|
command.indexBuffer = mesh.indexBuffer;
|
|
14799
|
+
command.indexFormat = mesh.indexFormat;
|
|
14412
14800
|
command.indexed = true;
|
|
14413
14801
|
command.count = mesh.indexCount;
|
|
14414
14802
|
command.instances = instances;
|
|
@@ -14421,6 +14809,78 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14421
14809
|
* light alone and its colour alone into the blur's targets and test for the depth its frame half
|
|
14422
14810
|
* wrote.
|
|
14423
14811
|
*/
|
|
14812
|
+
/**
|
|
14813
|
+
* Whether a split skin draw's two kept halves have pipelines, starting the compile of either that
|
|
14814
|
+
* has not: a skin drawn without them would lose its diffuse light for the frames they take, so a
|
|
14815
|
+
* split skin draws all three halves or none. See `PipelineCache.lit`.
|
|
14816
|
+
*/
|
|
14817
|
+
private skinHalvesReady(
|
|
14818
|
+
frameKey: string,
|
|
14819
|
+
base: string,
|
|
14820
|
+
skinned: boolean,
|
|
14821
|
+
morphed: boolean,
|
|
14822
|
+
layer: number,
|
|
14823
|
+
eight: boolean,
|
|
14824
|
+
clothed: boolean,
|
|
14825
|
+
): boolean {
|
|
14826
|
+
let ready = true;
|
|
14827
|
+
for (const half of SKIN_KEPT_HALVES) {
|
|
14828
|
+
const pipeline = this.skinHalfPipeline(
|
|
14829
|
+
half,
|
|
14830
|
+
frameKey,
|
|
14831
|
+
base,
|
|
14832
|
+
skinned,
|
|
14833
|
+
morphed,
|
|
14834
|
+
layer,
|
|
14835
|
+
eight,
|
|
14836
|
+
clothed,
|
|
14837
|
+
);
|
|
14838
|
+
if (pipeline === null) ready = false;
|
|
14839
|
+
}
|
|
14840
|
+
return ready;
|
|
14841
|
+
}
|
|
14842
|
+
|
|
14843
|
+
/** One kept half's pipeline, or none where it is still compiling: see `PipelineCache.lit`. */
|
|
14844
|
+
private skinHalfPipeline(
|
|
14845
|
+
half: (typeof SKIN_KEPT_HALVES)[number],
|
|
14846
|
+
frameKey: string,
|
|
14847
|
+
base: string,
|
|
14848
|
+
skinned: boolean,
|
|
14849
|
+
morphed: boolean,
|
|
14850
|
+
layer: number,
|
|
14851
|
+
eight: boolean,
|
|
14852
|
+
clothed: boolean,
|
|
14853
|
+
): GPURenderPipeline | null {
|
|
14854
|
+
const pipelines = this.skinPipelines;
|
|
14855
|
+
const present = this.meshPresent.get(base);
|
|
14856
|
+
if (pipelines === null || present === undefined) return null;
|
|
14857
|
+
const key = `${frameKey}|${half === 'diffuse' ? 'sd' : 'sa'}`;
|
|
14858
|
+
return (
|
|
14859
|
+
pipelines.peek(key) ??
|
|
14860
|
+
flatPipeline(
|
|
14861
|
+
pipelines,
|
|
14862
|
+
this.surface.device,
|
|
14863
|
+
this.flatLayoutFor(skinned, morphed),
|
|
14864
|
+
this.variant,
|
|
14865
|
+
key,
|
|
14866
|
+
present,
|
|
14867
|
+
false,
|
|
14868
|
+
skinned,
|
|
14869
|
+
morphed,
|
|
14870
|
+
true,
|
|
14871
|
+
layer,
|
|
14872
|
+
false,
|
|
14873
|
+
'none',
|
|
14874
|
+
this.materialDoubleSided,
|
|
14875
|
+
false,
|
|
14876
|
+
eight,
|
|
14877
|
+
clothed,
|
|
14878
|
+
'skin',
|
|
14879
|
+
half,
|
|
14880
|
+
)
|
|
14881
|
+
);
|
|
14882
|
+
}
|
|
14883
|
+
|
|
14424
14884
|
private keepSkinDiffuse(
|
|
14425
14885
|
mesh: GpuMesh,
|
|
14426
14886
|
frameKey: string,
|
|
@@ -14446,30 +14906,18 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14446
14906
|
const bindGroup = this.flatBindGroupFor(skinned, morphed, deltas);
|
|
14447
14907
|
/* The light alone, which the blur spreads, then the colour alone, which it is multiplied by. */
|
|
14448
14908
|
for (const half of SKIN_KEPT_HALVES) {
|
|
14449
|
-
const
|
|
14450
|
-
|
|
14451
|
-
|
|
14452
|
-
|
|
14453
|
-
|
|
14454
|
-
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
14459
|
-
|
|
14460
|
-
|
|
14461
|
-
morphed,
|
|
14462
|
-
true,
|
|
14463
|
-
layer,
|
|
14464
|
-
false,
|
|
14465
|
-
'none',
|
|
14466
|
-
this.materialDoubleSided,
|
|
14467
|
-
false,
|
|
14468
|
-
eight,
|
|
14469
|
-
clothed,
|
|
14470
|
-
'skin',
|
|
14471
|
-
half,
|
|
14472
|
-
);
|
|
14909
|
+
const pipeline = this.skinHalfPipeline(
|
|
14910
|
+
half,
|
|
14911
|
+
frameKey,
|
|
14912
|
+
base,
|
|
14913
|
+
skinned,
|
|
14914
|
+
morphed,
|
|
14915
|
+
layer,
|
|
14916
|
+
eight,
|
|
14917
|
+
clothed,
|
|
14918
|
+
);
|
|
14919
|
+
/* Both landed before the draw was let through: see `skinHalvesReady`. */
|
|
14920
|
+
if (pipeline === null) return;
|
|
14473
14921
|
const command = half === 'diffuse' ? skin.take() : skin.takeAlbedo();
|
|
14474
14922
|
command.pipeline = pipeline;
|
|
14475
14923
|
command.bindGroup = bindGroup;
|
|
@@ -14483,6 +14931,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14483
14931
|
}
|
|
14484
14932
|
command.vertexCount = mesh.vertexBuffers.length;
|
|
14485
14933
|
command.indexBuffer = mesh.indexBuffer;
|
|
14934
|
+
command.indexFormat = mesh.indexFormat;
|
|
14486
14935
|
command.indexed = true;
|
|
14487
14936
|
command.count = mesh.indexCount;
|
|
14488
14937
|
command.instances = 1;
|
|
@@ -14936,6 +15385,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14936
15385
|
command.offsetCount = 0;
|
|
14937
15386
|
command.vertexCount = 0;
|
|
14938
15387
|
command.indexBuffer = null;
|
|
15388
|
+
command.indexFormat = 'uint32';
|
|
14939
15389
|
command.indexed = false;
|
|
14940
15390
|
command.instances = 1;
|
|
14941
15391
|
command.indirect = null;
|
|
@@ -14995,7 +15445,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14995
15445
|
if (buffer != null) pass.setVertexBuffer(i, buffer);
|
|
14996
15446
|
}
|
|
14997
15447
|
if (command.indexed && command.indexBuffer !== null) {
|
|
14998
|
-
pass.setIndexBuffer(command.indexBuffer,
|
|
15448
|
+
pass.setIndexBuffer(command.indexBuffer, command.indexFormat);
|
|
14999
15449
|
if (command.indirect !== null) pass.drawIndexedIndirect(command.indirect, 0);
|
|
15000
15450
|
else pass.drawIndexed(command.count, command.instances);
|
|
15001
15451
|
} else {
|
|
@@ -16781,6 +17231,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16781
17231
|
this.rebuildRushBindGroup();
|
|
16782
17232
|
}
|
|
16783
17233
|
|
|
17234
|
+
/**
|
|
17235
|
+
* Where ambient occlusion fades out with distance. **The other backend's
|
|
17236
|
+
* `setAmbientOcclusionFade` carries the reasoning**; this is the same setter over the same state.
|
|
17237
|
+
*/
|
|
17238
|
+
setAmbientOcclusionFade(distance: number, radius: number): void {
|
|
17239
|
+
resolveOcclusionFade(distance, radius, this.aoFade);
|
|
17240
|
+
}
|
|
17241
|
+
|
|
16784
17242
|
/**
|
|
16785
17243
|
* Where this frame's lens is focused, how deep the sharp zone is, and how much of the ceiling
|
|
16786
17244
|
* to take. **The other backend's `setDepthOfField` carries the reasoning**, whole: the
|