@driftengine/core 4.8.1 → 4.8.3
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 +142 -0
- package/README.md +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -0
- package/dist/input/actionMap.d.ts +6 -1
- package/dist/input/actionMap.js +20 -7
- package/dist/input/input.d.ts +28 -0
- package/dist/input/input.js +71 -9
- package/dist/input/inputHarness.d.ts +9 -0
- package/dist/input/inputHarness.js +34 -2
- package/dist/render/backend/materialChanges.d.ts +7 -2
- package/dist/render/backend/materialChanges.js +8 -3
- package/dist/render/backend/webgl2/renderer.d.ts +75 -3
- package/dist/render/backend/webgl2/renderer.js +209 -22
- package/dist/render/backend/webgpu/buffers.d.ts +2 -0
- package/dist/render/backend/webgpu/buffers.js +2 -0
- package/dist/render/backend/webgpu/causticsPass.js +2 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +13 -3
- package/dist/render/backend/webgpu/depthPass.js +4 -3
- package/dist/render/backend/webgpu/filmPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +5 -0
- package/dist/render/backend/webgpu/flatPass.js +18 -8
- package/dist/render/backend/webgpu/linePass.js +2 -1
- package/dist/render/backend/webgpu/panelPass.js +2 -1
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/pipelineCache.d.ts +40 -8
- package/dist/render/backend/webgpu/pipelineCache.js +76 -9
- package/dist/render/backend/webgpu/plumePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +12 -2
- package/dist/render/backend/webgpu/postPass.js +19 -5
- package/dist/render/backend/webgpu/renderer.d.ts +66 -2
- package/dist/render/backend/webgpu/renderer.js +189 -38
- package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
- package/dist/render/backend/webgpu/textPass.js +2 -1
- package/dist/render/backend/webgpu/waterPass.js +2 -1
- package/dist/render/backend/webgpu/windStreakPass.js +2 -1
- package/dist/render/boltBatch.js +2 -1
- package/dist/render/causticsRenderer.js +2 -1
- package/dist/render/cutoutCaster.d.ts +3 -1
- package/dist/render/cutoutCaster.js +4 -0
- package/dist/render/decalPass.js +2 -1
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/lineBatch.js +5 -1
- package/dist/render/mapsWithoutUvs.d.ts +24 -0
- package/dist/render/mapsWithoutUvs.js +24 -0
- package/dist/render/mesh.d.ts +2 -0
- package/dist/render/mesh.js +3 -0
- package/dist/render/oitPass.js +2 -1
- package/dist/render/particleBatch.js +3 -2
- package/dist/render/particlePool.d.ts +21 -2
- package/dist/render/particlePool.js +72 -37
- package/dist/render/pass.d.ts +12 -0
- package/dist/render/plumeRenderer.js +3 -2
- package/dist/render/renderQuality.d.ts +16 -8
- package/dist/render/sceneCoverage.d.ts +54 -0
- package/dist/render/sceneCoverage.js +72 -0
- package/dist/render/sdfTextRenderer.js +2 -1
- package/dist/render/shaders/depth.js +3 -2
- package/dist/render/shaders/flat/glassTint.d.ts +28 -0
- package/dist/render/shaders/flat/glassTint.js +31 -0
- package/dist/render/shaders/flat/index.d.ts +12 -0
- package/dist/render/shaders/flat/index.js +13 -2
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +60 -25
- package/dist/render/shaders/flat/surfaceEffects.d.ts +1 -1
- package/dist/render/shaders/flat/surfaceEffects.js +6 -2
- package/dist/render/shaders/generated/depth.wgsl.d.ts +32 -12
- package/dist/render/shaders/generated/depth.wgsl.js +32 -12
- package/dist/render/shaders/generated/flat.wgsl.d.ts +89 -0
- package/dist/render/shaders/generated/flat.wgsl.js +127 -38
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
- package/dist/render/shaders/generated/particle.wgsl.js +3 -3
- package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/rush.wgsl.js +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +14 -1
- package/dist/render/shaders/outputTransform.js +23 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
- package/dist/render/shaders/rush.js +13 -2
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/temporalResolve.js +12 -5
- package/dist/render/ssrPass.js +2 -1
- package/dist/render/surfaceTexture.d.ts +13 -0
- package/dist/render/surfaceTexture.js +4 -0
- package/dist/render/textRenderer.js +2 -1
- package/dist/render/vertexDefaults.js +1 -0
- package/dist/render/viewModel.d.ts +29 -0
- package/dist/render/viewModel.js +35 -0
- package/dist/render/waterRenderer.js +2 -1
- package/dist/render/windStreakRenderer.js +2 -1
- package/package.json +6 -6
- package/src/index.ts +22 -1
- package/src/input/actionMap.ts +23 -8
- package/src/input/input.ts +79 -9
- package/src/input/inputHarness.ts +27 -2
- package/src/render/backend/materialChanges.ts +8 -3
- package/src/render/backend/webgl2/renderer.ts +223 -29
- package/src/render/backend/webgpu/buffers.ts +5 -0
- package/src/render/backend/webgpu/causticsPass.ts +2 -1
- package/src/render/backend/webgpu/depthPass.ts +4 -3
- package/src/render/backend/webgpu/filmPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +43 -26
- package/src/render/backend/webgpu/linePass.ts +2 -1
- package/src/render/backend/webgpu/panelPass.ts +2 -1
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/pipelineCache.ts +82 -8
- package/src/render/backend/webgpu/plumePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +20 -5
- package/src/render/backend/webgpu/renderer.ts +191 -37
- package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
- package/src/render/backend/webgpu/textPass.ts +2 -1
- package/src/render/backend/webgpu/waterPass.ts +2 -1
- package/src/render/backend/webgpu/windStreakPass.ts +2 -1
- package/src/render/boltBatch.ts +2 -1
- package/src/render/causticsRenderer.ts +2 -1
- package/src/render/cutoutCaster.ts +15 -2
- package/src/render/decalPass.ts +2 -1
- package/src/render/globalMediumPass.ts +3 -2
- package/src/render/lineBatch.ts +3 -1
- package/src/render/mapsWithoutUvs.ts +43 -0
- package/src/render/mesh.ts +3 -0
- package/src/render/oitPass.ts +2 -1
- package/src/render/particleBatch.ts +3 -2
- package/src/render/particlePool.ts +107 -56
- package/src/render/pass.ts +12 -0
- package/src/render/plumeRenderer.ts +3 -2
- package/src/render/renderQuality.ts +16 -8
- package/src/render/sceneCoverage.ts +78 -0
- package/src/render/sdfTextRenderer.ts +2 -1
- package/src/render/shaders/depth.ts +3 -2
- package/src/render/shaders/flat/glassTint.ts +48 -0
- package/src/render/shaders/flat/index.ts +25 -2
- package/src/render/shaders/flat/main.ts +60 -25
- package/src/render/shaders/flat/surfaceEffects.ts +6 -2
- package/src/render/shaders/generated/depth.wgsl.ts +32 -12
- package/src/render/shaders/generated/flat.wgsl.ts +127 -38
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +3 -3
- package/src/render/shaders/generated/rush.wgsl.ts +1 -1
- package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/outputTransform.ts +24 -1
- package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
- package/src/render/shaders/rush.ts +13 -2
- package/src/render/shaders/temporalResolve.ts +12 -5
- package/src/render/ssrPass.ts +2 -1
- package/src/render/surfaceTexture.ts +16 -0
- package/src/render/textRenderer.ts +2 -1
- package/src/render/vertexDefaults.ts +1 -0
- package/src/render/viewModel.ts +41 -0
- package/src/render/waterRenderer.ts +2 -1
- package/src/render/windStreakRenderer.ts +2 -1
|
@@ -1113,8 +1113,17 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1113
1113
|
private blankAlbedoBindGroup;
|
|
1114
1114
|
/** Repeats across the mesh's own UV range. Per draw, because `uUvScale` is a vertex uniform. */
|
|
1115
1115
|
private readonly uvScale;
|
|
1116
|
+
/** Where the texture starts, after the scale. Per draw, beside `uvScale`. */
|
|
1117
|
+
private readonly uvOffset;
|
|
1118
|
+
/**
|
|
1119
|
+
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
1120
|
+
* each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
|
|
1121
|
+
*/
|
|
1122
|
+
private surfaceFog;
|
|
1116
1123
|
/** Whether the bound material is seen from both faces. See `SurfaceMaterial.doubleSided`. */
|
|
1117
1124
|
private materialDoubleSided;
|
|
1125
|
+
/** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
|
|
1126
|
+
private materialHasMaps;
|
|
1118
1127
|
createSurfaceTexture(source: TexImageSource, options?: SurfaceTextureOptions): GpuSurfaceTexture;
|
|
1119
1128
|
/** Several images of one size as an array's layers. See `RendererApi.createSurfaceTextureArray`. */
|
|
1120
1129
|
createSurfaceTextureArray(sources: readonly TexImageSource[], options?: SurfaceTextureOptions): GpuSurfaceTexture;
|
|
@@ -1196,6 +1205,8 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1196
1205
|
/** How bright the environment the surfaces drawn next reflect is. See `renderer.ts`. */
|
|
1197
1206
|
setEnvironmentGain(gain: number): void;
|
|
1198
1207
|
setSurfaceGrain(amount: number): void;
|
|
1208
|
+
/** Whether the surfaces drawn next meet the medium. See `renderer.ts`. */
|
|
1209
|
+
setSurfaceFog(enabled: boolean): void;
|
|
1199
1210
|
/**
|
|
1200
1211
|
* How much of the surfaces drawn next survives a screen-door dither. See `renderer.ts`.
|
|
1201
1212
|
*
|
|
@@ -1814,7 +1825,7 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1814
1825
|
*/
|
|
1815
1826
|
/** The cutout bind group for `texture` in the open layer, built the first time it casts there. */
|
|
1816
1827
|
private cutoutGroup;
|
|
1817
|
-
/** The cutoff and UV scale into a cutout draw's slot, at
|
|
1828
|
+
/** The cutoff and UV scale and offset into a cutout draw's slot, at its own variant's offsets. */
|
|
1818
1829
|
private writeCutout;
|
|
1819
1830
|
private shadowGroup;
|
|
1820
1831
|
/**
|
|
@@ -1834,6 +1845,16 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1834
1845
|
*/
|
|
1835
1846
|
/** The glass the open shadow pass was offered, kept for its own passes: `glassCasters.ts`. */
|
|
1836
1847
|
private readonly glassCasters;
|
|
1848
|
+
/** Whether a glass caster has ever been offered, which is when lit pipelines start reading it. */
|
|
1849
|
+
private glassSeen;
|
|
1850
|
+
/**
|
|
1851
|
+
* The first glass caster switches every lit pipeline to reading glass. See
|
|
1852
|
+
* `PipelineCache.litSwitches`: until a pane exists the lookups would find nothing, and they cost
|
|
1853
|
+
* the lit pass on every surface whether they find anything or not.
|
|
1854
|
+
*/
|
|
1855
|
+
private noteGlassCaster;
|
|
1856
|
+
/** One of the lit stage's switches on, in every cache a lit pipeline can come from. */
|
|
1857
|
+
private enableLit;
|
|
1837
1858
|
/** One resolution every glass caster is read through, so recording one allocates nothing. */
|
|
1838
1859
|
private readonly glassCasterScratch;
|
|
1839
1860
|
private readonly casterSink;
|
|
@@ -2944,6 +2965,22 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
2944
2965
|
*/
|
|
2945
2966
|
private canDraw;
|
|
2946
2967
|
private openPass;
|
|
2968
|
+
private openAnyPass;
|
|
2969
|
+
/**
|
|
2970
|
+
* Submit what is drawn after the present on a microtask, which runs before the browser presents.
|
|
2971
|
+
*
|
|
2972
|
+
* There is no second `endFrame` to hang this on — a consumer thinks the frame is over — and
|
|
2973
|
+
* presentation happens when the task ends, after the microtask checkpoint. Anything that needs
|
|
2974
|
+
* the pixels sooner flushes synchronously; `copyRegionTo` is the one that does.
|
|
2975
|
+
*
|
|
2976
|
+
* **Queued wherever such work begins: where the overlay pass opens, and where a verb records.**
|
|
2977
|
+
* With the graph on a verb records rather than opening a pass, and until 4.8.2 only an opening
|
|
2978
|
+
* queued this, so `fillPanel` and a contributed pass drawn after `endFrame` waited for the next
|
|
2979
|
+
* `beginFrame` — whose replay drew onto a canvas texture already presented and destroyed.
|
|
2980
|
+
*/
|
|
2981
|
+
private queueOverlayFlush;
|
|
2982
|
+
/** `flushOverlay` bound once, so queueing it each frame allocates nothing. */
|
|
2983
|
+
private readonly flushOverlayBound;
|
|
2947
2984
|
/**
|
|
2948
2985
|
* The cache whose pipelines match the pass this draw will land in.
|
|
2949
2986
|
*
|
|
@@ -3159,6 +3196,15 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
3159
3196
|
* context is valid for the duration of the call for exactly this reason.
|
|
3160
3197
|
*/
|
|
3161
3198
|
private readonly passContext;
|
|
3199
|
+
/**
|
|
3200
|
+
* Fill the context a contributed pass draws with, for the pass it is about to draw into.
|
|
3201
|
+
*
|
|
3202
|
+
* **The attachments as well as the encoder**: a pass drawn after `endFrame` lands on the canvas
|
|
3203
|
+
* at one sample where the frame's own is the scene target at the scene's samples, and a pipeline
|
|
3204
|
+
* built for the one is refused by the other. The cache a mesh drawn here would use is the answer,
|
|
3205
|
+
* so the two cannot disagree.
|
|
3206
|
+
*/
|
|
3207
|
+
private fillPassContext;
|
|
3162
3208
|
/**
|
|
3163
3209
|
* Let something outside this file draw into the frame. See `Renderer.registerPass`.
|
|
3164
3210
|
*
|
|
@@ -3313,9 +3359,27 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
3313
3359
|
* **The clear is a drawn quad rather than a clear**, because WebGPU clears whole attachments
|
|
3314
3360
|
* and cannot confine one to a scissor. See `insetPass.ts`.
|
|
3315
3361
|
*
|
|
3316
|
-
* Returns the
|
|
3362
|
+
* Returns the **aspect** of the inset, width over height in the pixels its viewport got, as the
|
|
3363
|
+
* other backend does — for `camera.updateMatrices`. With no pass to draw into, a lost device,
|
|
3364
|
+
* it is the aspect of the box asked for: there are no pixels to measure, and a zero would be a
|
|
3365
|
+
* projection of infinities in the caller's camera.
|
|
3317
3366
|
*/
|
|
3318
3367
|
beginInset(rect: InsetRect, clearColor: Vec3 | null): number;
|
|
3368
|
+
/**
|
|
3369
|
+
* Draw what follows as a **view model**, in the nearest sliver of depth, until `endViewModel`.
|
|
3370
|
+
*
|
|
3371
|
+
* The twin of the WebGL2 method, which carries the reasons. Here the squeeze is the viewport's
|
|
3372
|
+
* depth bounds, which are pass state applied when a draw is issued, so both ends flush the graph
|
|
3373
|
+
* as an inset's do, and a pass opened in between is given the bounds again in `openPass`.
|
|
3374
|
+
*/
|
|
3375
|
+
beginViewModel(share?: number): void;
|
|
3376
|
+
/** Give the whole depth range back. Safe to call without a matching `beginViewModel`. */
|
|
3377
|
+
endViewModel(): void;
|
|
3378
|
+
/** The view model's depth bounds on the whole of the pass. */
|
|
3379
|
+
private squeezeViewport;
|
|
3380
|
+
/** Whether a view model is being drawn: `openPass` re-applies it, `endFrame` closes it. */
|
|
3381
|
+
private viewModelActive;
|
|
3382
|
+
private readonly viewModelRange;
|
|
3319
3383
|
/** Give the whole canvas back. Safe to call without a matching `beginInset`. */
|
|
3320
3384
|
endInset(): void;
|
|
3321
3385
|
/**
|
|
@@ -61,7 +61,7 @@ import { ExposurePass } from './exposurePass.js';
|
|
|
61
61
|
import { AO_STORE } from '../../shaders/ambientOcclusion.js';
|
|
62
62
|
import { clampAutoExposure } from '../../shaders/exposure.js';
|
|
63
63
|
import { clampLocalExposure } from '../../shaders/localExposure.js';
|
|
64
|
-
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, ADDITIVE_BLEND, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
64
|
+
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, ADDITIVE_BLEND, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, REFLECTION_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
65
65
|
import { GRADE_PLACEHOLDER_SIZE, identityGradeLut, validateGradeLut, } from '../../colourGrade.js';
|
|
66
66
|
import { FILM_LOOK_WITHOUT_COMPOSITE, clampGrain, clampVignette, grainSeed, } from '../../shaders/filmLook.js';
|
|
67
67
|
import { createProbeMipBindGroupLayout, createProbePrefilterBindGroupLayout, PREFILTER_UNIFORM_OFFSETS, probeLevels, probeMipPipeline, probePrefilterPipeline, PROBE_FACES, PROBE_FACE_BASIS, PROBE_FAR_M, PROBE_FOV_DEG, PROBE_MIP_UNIFORM_SIZE, PROBE_NEAR_M, } from './probePass.js';
|
|
@@ -77,6 +77,8 @@ import { resolvePointLights } from '../../lightBudget.js';
|
|
|
77
77
|
import { resolveRenderQuality } from '../../renderQuality.js';
|
|
78
78
|
import { DEFAULT_GLOBAL_MEDIUM, mediumActive, mediumTargetSize, WEAK_GPU_MEDIUM_STEPS, resolveGlobalMedium, } from '../../globalMedium.js';
|
|
79
79
|
import { castsDepth, glassOf } from '../../shadowCasters.js';
|
|
80
|
+
import { materialHasMaps, noteMapsWithoutUvs } from '../../mapsWithoutUvs.js';
|
|
81
|
+
import { VIEW_MODEL_DEPTH_SHARE, viewModelDepthRange } from '../../viewModel.js';
|
|
80
82
|
import { GlassCasterList } from '../../glassCasters.js';
|
|
81
83
|
import { GpuGlassTint } from './glassTintPass.js';
|
|
82
84
|
import { SUN_DYNAMIC_LAYER, SUN_GLASS_MOVING_LAYER, SUN_GLASS_STATIC_LAYER, SUN_PEELED_LAYER, SUN_STATIC_LAYER, } from '../../shadowMap.js';
|
|
@@ -98,7 +100,7 @@ import { LIGHT_VOLUME_VERT_FIELDS, LIGHT_VOLUME_VERT_SIZE, createLightVolumeBind
|
|
|
98
100
|
import { aimReflection, createGpuReflection } from './reflectionPass.js';
|
|
99
101
|
import { FrameBudget } from '../budget.js';
|
|
100
102
|
import { MaterialChanges, ownsMaterial } from '../materialChanges.js';
|
|
101
|
-
import { FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
103
|
+
import { FOG_OFF, FOG_RECEDE, fogModeOf } from '../drawFog.js';
|
|
102
104
|
import { DYNAMIC_ALIGNMENT as DYNAMIC_UNIFORM_ALIGNMENT, UniformRing } from './uniformRing.js';
|
|
103
105
|
import { DecalQueue, MAX_DRAWN_DECALS } from '../../decalQueue.js';
|
|
104
106
|
import { DistanceFieldScene } from '../../gi/fieldScene.js';
|
|
@@ -2129,8 +2131,17 @@ export class WebGPURenderer {
|
|
|
2129
2131
|
blankAlbedoBindGroup;
|
|
2130
2132
|
/** Repeats across the mesh's own UV range. Per draw, because `uUvScale` is a vertex uniform. */
|
|
2131
2133
|
uvScale = new Float32Array([1, 1]);
|
|
2134
|
+
/** Where the texture starts, after the scale. Per draw, beside `uvScale`. */
|
|
2135
|
+
uvOffset = new Float32Array([0, 0]);
|
|
2136
|
+
/**
|
|
2137
|
+
* How the surfaces drawn next meet the medium: what `setSurfaceFog` set, and `FOG_RECEDE` from
|
|
2138
|
+
* each `bindMeshPass`. A translucent draw's own fog is measured against this and puts it back.
|
|
2139
|
+
*/
|
|
2140
|
+
surfaceFog = FOG_RECEDE;
|
|
2132
2141
|
/** Whether the bound material is seen from both faces. See `SurfaceMaterial.doubleSided`. */
|
|
2133
2142
|
materialDoubleSided = false;
|
|
2143
|
+
/** Whether the bound material has a map, for the warning a mesh with no coordinates earns. */
|
|
2144
|
+
materialHasMaps = false;
|
|
2134
2145
|
createSurfaceTexture(source, options = {}) {
|
|
2135
2146
|
return new GpuSurfaceTexture(this.surface.device, this.pipelines, source, options);
|
|
2136
2147
|
}
|
|
@@ -2256,7 +2267,10 @@ export class WebGPURenderer {
|
|
|
2256
2267
|
const albedo = material?.albedo ?? null;
|
|
2257
2268
|
this.uvScale[0] = material?.uScale ?? 1;
|
|
2258
2269
|
this.uvScale[1] = material?.vScale ?? 1;
|
|
2270
|
+
this.uvOffset[0] = material?.uOffset ?? 0;
|
|
2271
|
+
this.uvOffset[1] = material?.vOffset ?? 0;
|
|
2259
2272
|
this.materialDoubleSided = material?.doubleSided === true;
|
|
2273
|
+
this.materialHasMaps = materialHasMaps(material);
|
|
2260
2274
|
const normal = (material?.normal ?? null);
|
|
2261
2275
|
const orm = (material?.orm ?? null);
|
|
2262
2276
|
const emissiveMap = (material?.emissive ?? null);
|
|
@@ -2403,6 +2417,13 @@ export class WebGPURenderer {
|
|
|
2403
2417
|
f[this.materialField('uGrain')] = Math.min(1, Math.max(0, amount));
|
|
2404
2418
|
});
|
|
2405
2419
|
}
|
|
2420
|
+
/** Whether the surfaces drawn next meet the medium. See `renderer.ts`. */
|
|
2421
|
+
setSurfaceFog(enabled) {
|
|
2422
|
+
this.surfaceFog = enabled ? FOG_RECEDE : FOG_OFF;
|
|
2423
|
+
this.material((_f, i) => {
|
|
2424
|
+
i[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2406
2427
|
/**
|
|
2407
2428
|
* How much of the surfaces drawn next survives a screen-door dither. See `renderer.ts`.
|
|
2408
2429
|
*
|
|
@@ -4358,10 +4379,12 @@ export class WebGPURenderer {
|
|
|
4358
4379
|
groups.set(texture, group);
|
|
4359
4380
|
return group;
|
|
4360
4381
|
}
|
|
4361
|
-
/** The cutoff and UV scale into a cutout draw's slot, at
|
|
4382
|
+
/** The cutoff and UV scale and offset into a cutout draw's slot, at its own variant's offsets. */
|
|
4362
4383
|
writeCutout(slot, cutout, fields) {
|
|
4363
4384
|
this.shadowDraws.writeFloat(slot, fields.uUvScale.offset, cutout.u);
|
|
4364
4385
|
this.shadowDraws.writeFloat(slot, fields.uUvScale.offset + 4, cutout.v);
|
|
4386
|
+
this.shadowDraws.writeFloat(slot, fields.uUvOffset.offset, cutout.uOffset);
|
|
4387
|
+
this.shadowDraws.writeFloat(slot, fields.uUvOffset.offset + 4, cutout.vOffset);
|
|
4365
4388
|
this.shadowDraws.writeFloat(slot, fields.uAlphaCutout.offset, cutout.cutoff);
|
|
4366
4389
|
}
|
|
4367
4390
|
shadowGroup() {
|
|
@@ -4400,6 +4423,29 @@ export class WebGPURenderer {
|
|
|
4400
4423
|
*/
|
|
4401
4424
|
/** The glass the open shadow pass was offered, kept for its own passes: `glassCasters.ts`. */
|
|
4402
4425
|
glassCasters = new GlassCasterList();
|
|
4426
|
+
/** Whether a glass caster has ever been offered, which is when lit pipelines start reading it. */
|
|
4427
|
+
glassSeen = false;
|
|
4428
|
+
/**
|
|
4429
|
+
* The first glass caster switches every lit pipeline to reading glass. See
|
|
4430
|
+
* `PipelineCache.litSwitches`: until a pane exists the lookups would find nothing, and they cost
|
|
4431
|
+
* the lit pass on every surface whether they find anything or not.
|
|
4432
|
+
*/
|
|
4433
|
+
noteGlassCaster() {
|
|
4434
|
+
if (this.glassSeen)
|
|
4435
|
+
return;
|
|
4436
|
+
this.glassSeen = true;
|
|
4437
|
+
this.enableLit('GLASS_SHADOWS');
|
|
4438
|
+
}
|
|
4439
|
+
/** One of the lit stage's switches on, in every cache a lit pipeline can come from. */
|
|
4440
|
+
enableLit(feature) {
|
|
4441
|
+
if (this.pipelines.litSwitches[feature])
|
|
4442
|
+
return;
|
|
4443
|
+
void this.pipelines.enable(feature);
|
|
4444
|
+
if (this.overlayPipelines !== this.pipelines)
|
|
4445
|
+
void this.overlayPipelines.enable(feature);
|
|
4446
|
+
if (this.latePipelines !== this.pipelines)
|
|
4447
|
+
void this.latePipelines.enable(feature);
|
|
4448
|
+
}
|
|
4403
4449
|
/** One resolution every glass caster is read through, so recording one allocates nothing. */
|
|
4404
4450
|
glassCasterScratch = {
|
|
4405
4451
|
transmission: 0,
|
|
@@ -4412,6 +4458,7 @@ export class WebGPURenderer {
|
|
|
4412
4458
|
/* Glass: kept aside for its own two passes rather than dropped. See `glassCasters.ts`. */
|
|
4413
4459
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
4414
4460
|
this.glassCasters.recordMesh(mesh, model, material, this.glassCasterScratch);
|
|
4461
|
+
this.noteGlassCaster();
|
|
4415
4462
|
}
|
|
4416
4463
|
return;
|
|
4417
4464
|
}
|
|
@@ -4451,6 +4498,7 @@ export class WebGPURenderer {
|
|
|
4451
4498
|
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
4452
4499
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
4453
4500
|
this.glassCasters.recordInstanced(batch, data, material, this.glassCasterScratch);
|
|
4501
|
+
this.noteGlassCaster();
|
|
4454
4502
|
}
|
|
4455
4503
|
return;
|
|
4456
4504
|
}
|
|
@@ -4491,6 +4539,7 @@ export class WebGPURenderer {
|
|
|
4491
4539
|
if (!this.drawingGlassDepth && !castsDepth(material)) {
|
|
4492
4540
|
if (this.quality.glassShadows !== 'off' && glassOf(material, this.glassCasterScratch)) {
|
|
4493
4541
|
this.glassCasters.recordSkinned(mesh, model, palette, material, this.glassCasterScratch);
|
|
4542
|
+
this.noteGlassCaster();
|
|
4494
4543
|
}
|
|
4495
4544
|
return;
|
|
4496
4545
|
}
|
|
@@ -4833,7 +4882,7 @@ export class WebGPURenderer {
|
|
|
4833
4882
|
*/
|
|
4834
4883
|
/* Off compiles none of the glass lookup into a lit pipeline: see `PipelineCache.glassShadows`. */
|
|
4835
4884
|
const glassShadows = quality.glassShadows !== 'off';
|
|
4836
|
-
this.pipelines = new PipelineCache(device, quality.screenEffects ? sceneColorFormat(quality.hdrScene) : surface.format, this.samples, glassShadows);
|
|
4885
|
+
this.pipelines = new PipelineCache(device, quality.screenEffects ? sceneColorFormat(quality.hdrScene) : surface.format, this.samples, glassShadows, quality.clusteredLights);
|
|
4837
4886
|
/*
|
|
4838
4887
|
* The canvas' own state, for the pass `ensurePass` reopens after `endFrame`.
|
|
4839
4888
|
*
|
|
@@ -4845,11 +4894,11 @@ export class WebGPURenderer {
|
|
|
4845
4894
|
this.overlayPipelines =
|
|
4846
4895
|
this.pipelines.format === surface.format && this.samples === 1
|
|
4847
4896
|
? this.pipelines
|
|
4848
|
-
: new PipelineCache(device, surface.format, 1, glassShadows);
|
|
4897
|
+
: new PipelineCache(device, surface.format, 1, glassShadows, quality.clusteredLights);
|
|
4849
4898
|
this.latePipelines =
|
|
4850
4899
|
this.pipelines.format === RECON_HISTORY_FORMAT && this.samples === 1
|
|
4851
4900
|
? this.pipelines
|
|
4852
|
-
: new PipelineCache(device, RECON_HISTORY_FORMAT, 1, glassShadows);
|
|
4901
|
+
: new PipelineCache(device, RECON_HISTORY_FORMAT, 1, glassShadows, quality.clusteredLights);
|
|
4853
4902
|
this.issueBound = this.issueCommand.bind(this);
|
|
4854
4903
|
this.pipelineTargets =
|
|
4855
4904
|
this.overlayPipelines === this.pipelines
|
|
@@ -5123,7 +5172,11 @@ export class WebGPURenderer {
|
|
|
5123
5172
|
/* The albedo's effects table, or a one-texel stand-in the shader's size test reads as none.
|
|
5124
5173
|
The albedo is the group's key, so its table needs no key of its own. */
|
|
5125
5174
|
if (name === 'uSurfaceEffects') {
|
|
5126
|
-
|
|
5175
|
+
const effects = this.albedo?.effectsView ?? null;
|
|
5176
|
+
/* The first table bound is when the lit pipelines start reading one. See `litSwitches`. */
|
|
5177
|
+
if (effects !== null)
|
|
5178
|
+
this.enableLit('SURFACE_EFFECTS');
|
|
5179
|
+
return { view: effects ?? blankView, sampler: blankSampler };
|
|
5127
5180
|
}
|
|
5128
5181
|
/*
|
|
5129
5182
|
* The room, once a bake has filled it. Unbaked reads as the blank cube: nothing to mirror.
|
|
@@ -6213,8 +6266,10 @@ export class WebGPURenderer {
|
|
|
6213
6266
|
* these per draw through the material block, the same shape `uOpacity` already uses.
|
|
6214
6267
|
*/
|
|
6215
6268
|
i[at('uLightingEnabled')] = 1;
|
|
6216
|
-
i[at('uFogEnabled')] =
|
|
6269
|
+
i[at('uFogEnabled')] = FOG_RECEDE;
|
|
6270
|
+
this.surfaceFog = FOG_RECEDE;
|
|
6217
6271
|
i[at('uAlbedoEnabled')] = 0;
|
|
6272
|
+
this.materialHasMaps = false;
|
|
6218
6273
|
/*
|
|
6219
6274
|
* **The camera medium, which this pass did not upload until 2026-08-29.**
|
|
6220
6275
|
*
|
|
@@ -6630,6 +6685,8 @@ export class WebGPURenderer {
|
|
|
6630
6685
|
this.bindGroup = this.blankAlbedoBindGroup;
|
|
6631
6686
|
this.uvScale[0] = 1;
|
|
6632
6687
|
this.uvScale[1] = 1;
|
|
6688
|
+
this.uvOffset[0] = 0;
|
|
6689
|
+
this.uvOffset[1] = 0;
|
|
6633
6690
|
}
|
|
6634
6691
|
/**
|
|
6635
6692
|
* The slot the next draw's material lives in, taking a new one when something dirtied it.
|
|
@@ -6707,7 +6764,11 @@ export class WebGPURenderer {
|
|
|
6707
6764
|
*/
|
|
6708
6765
|
if (this.tracksMotionNow())
|
|
6709
6766
|
this.recordDrawMotion(mesh, model, previousModel);
|
|
6710
|
-
|
|
6767
|
+
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
6768
|
+
/* An opaque draw meets the medium as the dial says: `submitMesh` reads an absent `fog` as
|
|
6769
|
+
true, which is a translucent draw's default and would put every opaque draw back in the fog
|
|
6770
|
+
`setSurfaceFog(false)` took it out of. */
|
|
6771
|
+
this.submitMesh(mesh, model, tint, 1, false, { depthLayer, fog: this.surfaceFog !== FOG_OFF });
|
|
6711
6772
|
}
|
|
6712
6773
|
/**
|
|
6713
6774
|
* Draw a mesh that lets the world through, at `opacity`.
|
|
@@ -6801,6 +6862,7 @@ export class WebGPURenderer {
|
|
|
6801
6862
|
this.translucentQueue.record(mesh, model, opacity, options);
|
|
6802
6863
|
return;
|
|
6803
6864
|
}
|
|
6865
|
+
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
6804
6866
|
/* The tint travels in the options here, where `drawMesh` takes it positionally — see
|
|
6805
6867
|
`TranslucentMeshOptions.tint` for why, and for the fade it was added for. */
|
|
6806
6868
|
this.submitMesh(mesh, model, options.tint ?? null, Math.min(opacity, 1), true, options);
|
|
@@ -6856,6 +6918,7 @@ export class WebGPURenderer {
|
|
|
6856
6918
|
/* The material's repeats, not a constant: geometry authored in metres is textured at
|
|
6857
6919
|
whatever density the material asks for without rebuilding it. */
|
|
6858
6920
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvScale.offset, this.uvScale);
|
|
6921
|
+
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvOffset.offset, this.uvOffset);
|
|
6859
6922
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uLightViewProj.offset, this.lightViewProj);
|
|
6860
6923
|
/*
|
|
6861
6924
|
* Whether this mesh's tangent frame is real. WebGPU has no disabled attribute, so location 10
|
|
@@ -6888,7 +6951,7 @@ export class WebGPURenderer {
|
|
|
6888
6951
|
/* Added light fades in the medium rather than receding into it. See `drawFog.ts`. */
|
|
6889
6952
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
6890
6953
|
const toneMapped = options.toneMapped ?? true;
|
|
6891
|
-
const unlitOrUnfogged = !lit || fog !==
|
|
6954
|
+
const unlitOrUnfogged = !lit || fog !== this.surfaceFog;
|
|
6892
6955
|
if (unlitOrUnfogged) {
|
|
6893
6956
|
if (!lit)
|
|
6894
6957
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 0;
|
|
@@ -6911,7 +6974,7 @@ export class WebGPURenderer {
|
|
|
6911
6974
|
const refracting = this.bindSeeThrough(options);
|
|
6912
6975
|
/* Taken for itself when any of those differs from the pass, and put back below: the rule both
|
|
6913
6976
|
backends count material changes by. See `materialChanges.ts`. */
|
|
6914
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
6977
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
6915
6978
|
if (own)
|
|
6916
6979
|
this.materials.dirty();
|
|
6917
6980
|
const base = mesh.key ?? 'flat:s0:u0';
|
|
@@ -7041,7 +7104,7 @@ export class WebGPURenderer {
|
|
|
7041
7104
|
if (unlitOrUnfogged) {
|
|
7042
7105
|
if (!lit)
|
|
7043
7106
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 1;
|
|
7044
|
-
this.perFrameInts[this.materialField('uFogEnabled')] =
|
|
7107
|
+
this.perFrameInts[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
7045
7108
|
}
|
|
7046
7109
|
/* Back to off for every other draw in the frame, exactly as `uOpacity` is: this scratch is
|
|
7047
7110
|
shared, so a strength left set is worn by everything drawn after it. */
|
|
@@ -7246,6 +7309,7 @@ export class WebGPURenderer {
|
|
|
7246
7309
|
const lands = blend && options.reconstructed !== true && this.drawsLate();
|
|
7247
7310
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uViewProj.offset, lands ? this.correctedViewProj : this.viewProj);
|
|
7248
7311
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvScale.offset, this.uvScale);
|
|
7312
|
+
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uUvOffset.offset, this.uvOffset);
|
|
7249
7313
|
this.perDraw.writeFloats(slot, FLAT_VERT_FIELDS.uLightViewProj.offset, this.lightViewProj);
|
|
7250
7314
|
this.perDraw.writeInt(slot, FLAT_VERT_FIELDS.uHasTangents.offset, mesh.hasTangents ? 1 : 0);
|
|
7251
7315
|
const dimmed = opacity < 1;
|
|
@@ -7255,7 +7319,7 @@ export class WebGPURenderer {
|
|
|
7255
7319
|
const lit = options.lit ?? true;
|
|
7256
7320
|
const fog = fogModeOf(options.fog ?? true, blend && options.additive === true);
|
|
7257
7321
|
const toneMapped = options.toneMapped ?? true;
|
|
7258
|
-
const unlitOrUnfogged = !lit || fog !==
|
|
7322
|
+
const unlitOrUnfogged = !lit || fog !== this.surfaceFog;
|
|
7259
7323
|
if (unlitOrUnfogged) {
|
|
7260
7324
|
if (!lit)
|
|
7261
7325
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 0;
|
|
@@ -7268,7 +7332,7 @@ export class WebGPURenderer {
|
|
|
7268
7332
|
const layer = Math.min(Math.max(Math.round(options.depthLayer ?? 0), 0), MAX_DEPTH_LAYER);
|
|
7269
7333
|
/* A blended batch shows what is behind it — refraction or glass — as a single draw does. */
|
|
7270
7334
|
const refracting = blend && this.bindSeeThrough(options);
|
|
7271
|
-
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting });
|
|
7335
|
+
const own = ownsMaterial({ opacity, lit, fog, toneMapped, refracting }, this.surfaceFog);
|
|
7272
7336
|
if (own)
|
|
7273
7337
|
this.materials.dirty();
|
|
7274
7338
|
const base = mesh.key ?? 'flat:s0:u0';
|
|
@@ -7337,7 +7401,7 @@ export class WebGPURenderer {
|
|
|
7337
7401
|
}
|
|
7338
7402
|
if (unlitOrUnfogged) {
|
|
7339
7403
|
this.perFrameInts[this.materialField('uLightingEnabled')] = 1;
|
|
7340
|
-
this.perFrameInts[this.materialField('uFogEnabled')] =
|
|
7404
|
+
this.perFrameInts[this.materialField('uFogEnabled')] = this.surfaceFog;
|
|
7341
7405
|
}
|
|
7342
7406
|
if (!toneMapped) {
|
|
7343
7407
|
this.perFrameInts[this.materialField('uOutputTransform')] = this.gradeCode();
|
|
@@ -9545,7 +9609,7 @@ export class WebGPURenderer {
|
|
|
9545
9609
|
label: 'post.ssr.resolve',
|
|
9546
9610
|
colorAttachments: [{ view: scene, loadOp: 'load', storeOp: 'store' }],
|
|
9547
9611
|
});
|
|
9548
|
-
resolve.setPipeline(postPipeline(this.pipelines, device, this.ssrResolveLayout, 'post.ssrResolve', SSR_RESOLVE_FRAG_WGSL, this.pipelines.format,
|
|
9612
|
+
resolve.setPipeline(postPipeline(this.pipelines, device, this.ssrResolveLayout, 'post.ssrResolve', SSR_RESOLVE_FRAG_WGSL, this.pipelines.format, REFLECTION_OVER_BLEND));
|
|
9549
9613
|
resolve.setBindGroup(0, resolveGroup);
|
|
9550
9614
|
resolve.draw(3);
|
|
9551
9615
|
resolve.end();
|
|
@@ -10442,6 +10506,8 @@ export class WebGPURenderer {
|
|
|
10442
10506
|
command.instances = 1;
|
|
10443
10507
|
command.indirect = null;
|
|
10444
10508
|
recordNode(this.arena, VERB_DRAW, reads, writes, at, 0);
|
|
10509
|
+
if (this.framePresented)
|
|
10510
|
+
this.queueOverlayFlush();
|
|
10445
10511
|
return command;
|
|
10446
10512
|
}
|
|
10447
10513
|
/** Replay one recorded draw into an open pass. */
|
|
@@ -10457,11 +10523,7 @@ export class WebGPURenderer {
|
|
|
10457
10523
|
const definition = passAt(this.passes, nodeState(this.arena, at));
|
|
10458
10524
|
if (definition === undefined)
|
|
10459
10525
|
return;
|
|
10460
|
-
this.
|
|
10461
|
-
/* The frame's grade, so a contributed pass can obey the 2026-08-17 rule. See `PassContext`. */
|
|
10462
|
-
this.passContext.outputTransform = this.gradeCode();
|
|
10463
|
-
this.passContext.outputExposure = this.gradeExposure();
|
|
10464
|
-
definition.draw(this.passContext);
|
|
10526
|
+
definition.draw(this.fillPassContext(pass));
|
|
10465
10527
|
return;
|
|
10466
10528
|
}
|
|
10467
10529
|
if (verb !== VERB_DRAW)
|
|
@@ -10558,6 +10620,15 @@ export class WebGPURenderer {
|
|
|
10558
10620
|
return this.framePresented;
|
|
10559
10621
|
}
|
|
10560
10622
|
openPass() {
|
|
10623
|
+
const was = this.pass;
|
|
10624
|
+
const pass = this.openAnyPass();
|
|
10625
|
+
/* A pass opened while a view model is drawn starts with the whole depth range: squeeze it. */
|
|
10626
|
+
if (this.viewModelActive && pass !== null && pass !== was && !this.reflectionPassActive) {
|
|
10627
|
+
this.squeezeViewport(pass);
|
|
10628
|
+
}
|
|
10629
|
+
return pass;
|
|
10630
|
+
}
|
|
10631
|
+
openAnyPass() {
|
|
10561
10632
|
/* While the replay is running this is the only pass there is. See `oitEncoder`. */
|
|
10562
10633
|
if (this.oitEncoder !== null)
|
|
10563
10634
|
return this.oitEncoder;
|
|
@@ -10649,18 +10720,29 @@ export class WebGPURenderer {
|
|
|
10649
10720
|
},
|
|
10650
10721
|
});
|
|
10651
10722
|
this.overlayActive = true;
|
|
10652
|
-
|
|
10653
|
-
* **Submitted on a microtask, which runs before the browser presents.** There is no second
|
|
10654
|
-
* `endFrame` to hang this on — a consumer thinks the frame is over — and presentation happens
|
|
10655
|
-
* when the task ends, after the microtask checkpoint. Anything that needs the pixels sooner
|
|
10656
|
-
* flushes synchronously; `copyRegionTo` is the one that does.
|
|
10657
|
-
*/
|
|
10658
|
-
if (!this.overlayFlushQueued) {
|
|
10659
|
-
this.overlayFlushQueued = true;
|
|
10660
|
-
queueMicrotask(() => this.flushOverlay());
|
|
10661
|
-
}
|
|
10723
|
+
this.queueOverlayFlush();
|
|
10662
10724
|
return this.pass;
|
|
10663
10725
|
}
|
|
10726
|
+
/**
|
|
10727
|
+
* Submit what is drawn after the present on a microtask, which runs before the browser presents.
|
|
10728
|
+
*
|
|
10729
|
+
* There is no second `endFrame` to hang this on — a consumer thinks the frame is over — and
|
|
10730
|
+
* presentation happens when the task ends, after the microtask checkpoint. Anything that needs
|
|
10731
|
+
* the pixels sooner flushes synchronously; `copyRegionTo` is the one that does.
|
|
10732
|
+
*
|
|
10733
|
+
* **Queued wherever such work begins: where the overlay pass opens, and where a verb records.**
|
|
10734
|
+
* With the graph on a verb records rather than opening a pass, and until 4.8.2 only an opening
|
|
10735
|
+
* queued this, so `fillPanel` and a contributed pass drawn after `endFrame` waited for the next
|
|
10736
|
+
* `beginFrame` — whose replay drew onto a canvas texture already presented and destroyed.
|
|
10737
|
+
*/
|
|
10738
|
+
queueOverlayFlush() {
|
|
10739
|
+
if (this.overlayFlushQueued)
|
|
10740
|
+
return;
|
|
10741
|
+
this.overlayFlushQueued = true;
|
|
10742
|
+
queueMicrotask(this.flushOverlayBound);
|
|
10743
|
+
}
|
|
10744
|
+
/** `flushOverlay` bound once, so queueing it each frame allocates nothing. */
|
|
10745
|
+
flushOverlayBound = () => this.flushOverlay();
|
|
10664
10746
|
/**
|
|
10665
10747
|
* The cache whose pipelines match the pass this draw will land in.
|
|
10666
10748
|
*
|
|
@@ -10725,6 +10807,9 @@ export class WebGPURenderer {
|
|
|
10725
10807
|
this.encoder = null;
|
|
10726
10808
|
}
|
|
10727
10809
|
endFrame() {
|
|
10810
|
+
/* A view model left open would squeeze every pass after the world, the composite included. */
|
|
10811
|
+
if (this.viewModelActive)
|
|
10812
|
+
this.endViewModel();
|
|
10728
10813
|
this.inFrame = false;
|
|
10729
10814
|
if (this.surface.lost || this.encoder === null)
|
|
10730
10815
|
return;
|
|
@@ -11148,7 +11233,29 @@ export class WebGPURenderer {
|
|
|
11148
11233
|
outputTransform: 0,
|
|
11149
11234
|
outputExposure: 1,
|
|
11150
11235
|
jitter: this.passJitter,
|
|
11236
|
+
format: 'rgba8unorm',
|
|
11237
|
+
depthFormat: DEPTH_FORMAT,
|
|
11238
|
+
samples: 1,
|
|
11151
11239
|
};
|
|
11240
|
+
/**
|
|
11241
|
+
* Fill the context a contributed pass draws with, for the pass it is about to draw into.
|
|
11242
|
+
*
|
|
11243
|
+
* **The attachments as well as the encoder**: a pass drawn after `endFrame` lands on the canvas
|
|
11244
|
+
* at one sample where the frame's own is the scene target at the scene's samples, and a pipeline
|
|
11245
|
+
* built for the one is refused by the other. The cache a mesh drawn here would use is the answer,
|
|
11246
|
+
* so the two cannot disagree.
|
|
11247
|
+
*/
|
|
11248
|
+
fillPassContext(pass) {
|
|
11249
|
+
const target = this.targetPipelines();
|
|
11250
|
+
const context = this.passContext;
|
|
11251
|
+
context.pass = pass;
|
|
11252
|
+
/* The frame's grade, so a contributed pass can obey the 2026-08-17 rule. See `PassContext`. */
|
|
11253
|
+
context.outputTransform = this.gradeCode();
|
|
11254
|
+
context.outputExposure = this.gradeExposure();
|
|
11255
|
+
context.format = target.format;
|
|
11256
|
+
context.samples = target.sampleCount;
|
|
11257
|
+
return context;
|
|
11258
|
+
}
|
|
11152
11259
|
/**
|
|
11153
11260
|
* Let something outside this file draw into the frame. See `Renderer.registerPass`.
|
|
11154
11261
|
*
|
|
@@ -11286,14 +11393,12 @@ export class WebGPURenderer {
|
|
|
11286
11393
|
const pass = this.openPass();
|
|
11287
11394
|
if (pass === null)
|
|
11288
11395
|
return;
|
|
11289
|
-
this.
|
|
11290
|
-
/* The frame's grade, so a contributed pass can obey the 2026-08-17 rule. See `PassContext`. */
|
|
11291
|
-
this.passContext.outputTransform = this.gradeCode();
|
|
11292
|
-
this.passContext.outputExposure = this.gradeExposure();
|
|
11293
|
-
definition.draw(this.passContext);
|
|
11396
|
+
definition.draw(this.fillPassContext(pass));
|
|
11294
11397
|
return;
|
|
11295
11398
|
}
|
|
11296
11399
|
recordNode(this.arena, VERB_PASS, this.passReads.get(handle) ?? 0, this.currentTarget(), handle, 0);
|
|
11400
|
+
if (this.framePresented)
|
|
11401
|
+
this.queueOverlayFlush();
|
|
11297
11402
|
}
|
|
11298
11403
|
/** Let go of a pass. A handle kept past this draws nothing; see `PassHandle`. */
|
|
11299
11404
|
unregisterPass(handle) {
|
|
@@ -11485,7 +11590,10 @@ export class WebGPURenderer {
|
|
|
11485
11590
|
* **The clear is a drawn quad rather than a clear**, because WebGPU clears whole attachments
|
|
11486
11591
|
* and cannot confine one to a scissor. See `insetPass.ts`.
|
|
11487
11592
|
*
|
|
11488
|
-
* Returns the
|
|
11593
|
+
* Returns the **aspect** of the inset, width over height in the pixels its viewport got, as the
|
|
11594
|
+
* other backend does — for `camera.updateMatrices`. With no pass to draw into, a lost device,
|
|
11595
|
+
* it is the aspect of the box asked for: there are no pixels to measure, and a zero would be a
|
|
11596
|
+
* projection of infinities in the caller's camera.
|
|
11489
11597
|
*/
|
|
11490
11598
|
beginInset(rect, clearColor) {
|
|
11491
11599
|
/*
|
|
@@ -11497,7 +11605,7 @@ export class WebGPURenderer {
|
|
|
11497
11605
|
this.flushGraph();
|
|
11498
11606
|
const pass = this.openPass();
|
|
11499
11607
|
if (this.surface.lost || pass === null)
|
|
11500
|
-
return
|
|
11608
|
+
return Math.max(rect.width, 1) / Math.max(rect.height, 1);
|
|
11501
11609
|
const canvas = this.surface.context.canvas;
|
|
11502
11610
|
const cssWidth = Math.max(canvas.clientWidth, 1);
|
|
11503
11611
|
const cssHeight = Math.max(canvas.clientHeight, 1);
|
|
@@ -11594,6 +11702,44 @@ export class WebGPURenderer {
|
|
|
11594
11702
|
*/
|
|
11595
11703
|
return w / h;
|
|
11596
11704
|
}
|
|
11705
|
+
/**
|
|
11706
|
+
* Draw what follows as a **view model**, in the nearest sliver of depth, until `endViewModel`.
|
|
11707
|
+
*
|
|
11708
|
+
* The twin of the WebGL2 method, which carries the reasons. Here the squeeze is the viewport's
|
|
11709
|
+
* depth bounds, which are pass state applied when a draw is issued, so both ends flush the graph
|
|
11710
|
+
* as an inset's do, and a pass opened in between is given the bounds again in `openPass`.
|
|
11711
|
+
*/
|
|
11712
|
+
beginViewModel(share = VIEW_MODEL_DEPTH_SHARE) {
|
|
11713
|
+
if (this.quality.frameGraph)
|
|
11714
|
+
this.flushGraph();
|
|
11715
|
+
viewModelDepthRange(this.reversedDepth, share, this.viewModelRange);
|
|
11716
|
+
this.viewModelActive = true;
|
|
11717
|
+
const pass = this.openPass();
|
|
11718
|
+
if (this.surface.lost || pass === null)
|
|
11719
|
+
return;
|
|
11720
|
+
this.squeezeViewport(pass);
|
|
11721
|
+
}
|
|
11722
|
+
/** Give the whole depth range back. Safe to call without a matching `beginViewModel`. */
|
|
11723
|
+
endViewModel() {
|
|
11724
|
+
if (!this.viewModelActive)
|
|
11725
|
+
return;
|
|
11726
|
+
if (this.quality.frameGraph)
|
|
11727
|
+
this.flushGraph();
|
|
11728
|
+
this.viewModelActive = false;
|
|
11729
|
+
const pass = this.pass;
|
|
11730
|
+
if (this.surface.lost || pass === null)
|
|
11731
|
+
return;
|
|
11732
|
+
const [width, height] = this.passSize();
|
|
11733
|
+
pass.setViewport(0, 0, width, height, 0, 1);
|
|
11734
|
+
}
|
|
11735
|
+
/** The view model's depth bounds on the whole of the pass. */
|
|
11736
|
+
squeezeViewport(pass) {
|
|
11737
|
+
const [width, height] = this.passSize();
|
|
11738
|
+
pass.setViewport(0, 0, width, height, this.viewModelRange[0], this.viewModelRange[1]);
|
|
11739
|
+
}
|
|
11740
|
+
/** Whether a view model is being drawn: `openPass` re-applies it, `endFrame` closes it. */
|
|
11741
|
+
viewModelActive = false;
|
|
11742
|
+
viewModelRange = [0, 1];
|
|
11597
11743
|
/** Give the whole canvas back. Safe to call without a matching `beginInset`. */
|
|
11598
11744
|
endInset() {
|
|
11599
11745
|
/*
|
|
@@ -12663,6 +12809,9 @@ export class WebGPURenderer {
|
|
|
12663
12809
|
this.iesRows = this.iesAtlas.height;
|
|
12664
12810
|
this.iesPlanes = this.iesAtlas.planes;
|
|
12665
12811
|
this.cookieTiles = this.cookieImages.length;
|
|
12812
|
+
/* The first profile or cookie is when the lit pipelines start reading them. See `litSwitches`. */
|
|
12813
|
+
if (this.iesRows > 1 || this.cookieTiles > 0)
|
|
12814
|
+
this.enableLit('LIGHT_FIXTURES');
|
|
12666
12815
|
return this.fixtureView;
|
|
12667
12816
|
}
|
|
12668
12817
|
/**
|
|
@@ -12739,6 +12888,8 @@ export class WebGPURenderer {
|
|
|
12739
12888
|
device.queue.writeTexture({ texture: atlas }, field.atlas, { bytesPerRow: aw * 8, rowsPerImage: ah }, [aw, ah, ad]);
|
|
12740
12889
|
this.driftIndex = index;
|
|
12741
12890
|
this.driftAtlas = atlas;
|
|
12891
|
+
/* The first volume is when the lit pipelines start reading one. See `litSwitches`. */
|
|
12892
|
+
this.enableLit('DRIFT_LIGHT');
|
|
12742
12893
|
this.driftIndexView = index.createView({ dimension: '3d' });
|
|
12743
12894
|
this.driftAtlasView = atlas.createView({ dimension: '3d' });
|
|
12744
12895
|
this.bakeLightField = {
|
|
@@ -3,6 +3,7 @@ import { SDFTEXT_BINDINGS, SDF_TEXT_FRAG_WGSL, SDF_TEXT_VERT_WGSL, } from '../..
|
|
|
3
3
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
4
4
|
import { SdfTextLayout } from '../../sdfTextLayout.js';
|
|
5
5
|
import { shaderModule } from './shaderModules.js';
|
|
6
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
|
|
6
7
|
/**
|
|
7
8
|
* The SDF text pass: a string as flat, textured quads, drawn into the scene.
|
|
8
9
|
*
|
|
@@ -183,7 +184,7 @@ export function sdfTextPipeline(cache, device, layout) {
|
|
|
183
184
|
format: cache.format,
|
|
184
185
|
blend: {
|
|
185
186
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
186
|
-
alpha:
|
|
187
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
187
188
|
},
|
|
188
189
|
},
|
|
189
190
|
],
|
|
@@ -3,6 +3,7 @@ import { TEXT_BINDINGS, TEXT_FRAG_WGSL, TEXT_VERT_WGSL, } from '../../shaders/ge
|
|
|
3
3
|
import { MAX_CELLS, TEXT_CUBE, TextLayout } from '../../textLayout.js';
|
|
4
4
|
import { DEPTH_FORMAT } from './flatPass.js';
|
|
5
5
|
import { shaderModule } from './shaderModules.js';
|
|
6
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.js';
|
|
6
7
|
/**
|
|
7
8
|
* The text pass: a string as instanced glyph cubes, drawn over the scene.
|
|
8
9
|
*
|
|
@@ -174,7 +175,7 @@ export function textPipeline(cache, device, layout) {
|
|
|
174
175
|
format: cache.format,
|
|
175
176
|
blend: {
|
|
176
177
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
177
|
-
alpha:
|
|
178
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
178
179
|
},
|
|
179
180
|
},
|
|
180
181
|
],
|