@driftengine/core 4.8.7 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/CHANGELOG.json +61 -0
  2. package/README.md +1 -1
  3. package/dist/index.d.ts +5 -0
  4. package/dist/index.js +7 -0
  5. package/dist/render/atmosphere.d.ts +13 -0
  6. package/dist/render/atmosphere.js +9 -1
  7. package/dist/render/backend/api.d.ts +9 -1
  8. package/dist/render/backend/createRenderer.d.ts +6 -0
  9. package/dist/render/backend/createRenderer.js +4 -1
  10. package/dist/render/backend/webgl2/renderer.d.ts +90 -2
  11. package/dist/render/backend/webgl2/renderer.js +297 -16
  12. package/dist/render/backend/webgpu/device.d.ts +7 -1
  13. package/dist/render/backend/webgpu/device.js +31 -3
  14. package/dist/render/backend/webgpu/flatPass.d.ts +16 -1
  15. package/dist/render/backend/webgpu/flatPass.js +58 -26
  16. package/dist/render/backend/webgpu/postPass.d.ts +51 -9
  17. package/dist/render/backend/webgpu/postPass.js +16 -2
  18. package/dist/render/backend/webgpu/reflectionSurfacePass.d.ts +30 -0
  19. package/dist/render/backend/webgpu/reflectionSurfacePass.js +100 -0
  20. package/dist/render/backend/webgpu/renderer.d.ts +131 -2
  21. package/dist/render/backend/webgpu/renderer.js +586 -70
  22. package/dist/render/displayRange.d.ts +31 -0
  23. package/dist/render/displayRange.js +47 -0
  24. package/dist/render/filmicCurve.d.ts +74 -0
  25. package/dist/render/filmicCurve.js +203 -0
  26. package/dist/render/fog.d.ts +4 -1
  27. package/dist/render/fog.js +12 -2
  28. package/dist/render/frame/resources.d.ts +2 -2
  29. package/dist/render/frame/resources.js +6 -0
  30. package/dist/render/frameReflections.d.ts +40 -0
  31. package/dist/render/frameReflections.js +48 -0
  32. package/dist/render/pass.d.ts +49 -0
  33. package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
  34. package/dist/render/reflectionSurfaceTargets.js +99 -0
  35. package/dist/render/renderQuality.d.ts +39 -8
  36. package/dist/render/renderQuality.js +4 -0
  37. package/dist/render/sceneTarget.d.ts +5 -0
  38. package/dist/render/sceneTarget.js +24 -1
  39. package/dist/render/screenSpaceReflection.d.ts +5 -0
  40. package/dist/render/screenSpaceReflection.js +5 -0
  41. package/dist/render/shaders/arcane.d.ts +1 -1
  42. package/dist/render/shaders/bolt.d.ts +1 -1
  43. package/dist/render/shaders/caustics.d.ts +1 -1
  44. package/dist/render/shaders/film.d.ts +1 -1
  45. package/dist/render/shaders/filmLook.js +2 -1
  46. package/dist/render/shaders/fire.d.ts +1 -1
  47. package/dist/render/shaders/flat/glassTint.d.ts +1 -1
  48. package/dist/render/shaders/flat/glassTint.js +2 -0
  49. package/dist/render/shaders/flat/index.d.ts +10 -0
  50. package/dist/render/shaders/flat/index.js +6 -0
  51. package/dist/render/shaders/flat/main.d.ts +1 -1
  52. package/dist/render/shaders/flat/main.js +28 -0
  53. package/dist/render/shaders/flat/overlay.d.ts +16 -0
  54. package/dist/render/shaders/flat/overlay.js +117 -0
  55. package/dist/render/shaders/flat/pointShadow.js +1 -1
  56. package/dist/render/shaders/flat/reflectionSurface.d.ts +21 -0
  57. package/dist/render/shaders/flat/reflectionSurface.js +61 -0
  58. package/dist/render/shaders/fog.d.ts +1 -1
  59. package/dist/render/shaders/fog.js +9 -2
  60. package/dist/render/shaders/fringe.d.ts +29 -0
  61. package/dist/render/shaders/fringe.js +55 -0
  62. package/dist/render/shaders/generated/arcane.wgsl.d.ts +1 -1
  63. package/dist/render/shaders/generated/arcane.wgsl.js +1 -1
  64. package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
  65. package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
  66. package/dist/render/shaders/generated/caustics.wgsl.d.ts +1 -1
  67. package/dist/render/shaders/generated/caustics.wgsl.js +1 -1
  68. package/dist/render/shaders/generated/film.wgsl.d.ts +1 -1
  69. package/dist/render/shaders/generated/film.wgsl.js +1 -1
  70. package/dist/render/shaders/generated/fire.wgsl.d.ts +1 -1
  71. package/dist/render/shaders/generated/fire.wgsl.js +1 -1
  72. package/dist/render/shaders/generated/flat.wgsl.d.ts +176 -16
  73. package/dist/render/shaders/generated/flat.wgsl.js +214 -54
  74. package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
  75. package/dist/render/shaders/generated/line.wgsl.js +1 -1
  76. package/dist/render/shaders/generated/particle.wgsl.d.ts +4 -4
  77. package/dist/render/shaders/generated/particle.wgsl.js +4 -4
  78. package/dist/render/shaders/generated/rush.wgsl.d.ts +56 -26
  79. package/dist/render/shaders/generated/rush.wgsl.js +56 -26
  80. package/dist/render/shaders/generated/scatter.wgsl.d.ts +1 -1
  81. package/dist/render/shaders/generated/scatter.wgsl.js +1 -1
  82. package/dist/render/shaders/generated/smoke.wgsl.d.ts +1 -1
  83. package/dist/render/shaders/generated/smoke.wgsl.js +1 -1
  84. package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +29 -0
  85. package/dist/render/shaders/generated/ssrResolve.wgsl.js +29 -0
  86. package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +22 -2
  87. package/dist/render/shaders/generated/ssrTrace.wgsl.js +22 -2
  88. package/dist/render/shaders/generated/water.wgsl.d.ts +1 -1
  89. package/dist/render/shaders/generated/water.wgsl.js +1 -1
  90. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +2 -2
  91. package/dist/render/shaders/gpudriven/fog.wgsl.js +11 -3
  92. package/dist/render/shaders/line.d.ts +1 -1
  93. package/dist/render/shaders/particle.d.ts +3 -3
  94. package/dist/render/shaders/rush.js +54 -11
  95. package/dist/render/shaders/scatter.d.ts +1 -1
  96. package/dist/render/shaders/smoke.d.ts +1 -1
  97. package/dist/render/shaders/ssrResolve.d.ts +17 -0
  98. package/dist/render/shaders/ssrResolve.js +51 -0
  99. package/dist/render/shaders/ssrTrace.js +26 -0
  100. package/dist/render/shaders/water.d.ts +1 -1
  101. package/dist/render/ssrPass.d.ts +16 -0
  102. package/dist/render/ssrPass.js +106 -1
  103. package/dist/render/surfaceOverlay.d.ts +132 -0
  104. package/dist/render/surfaceOverlay.js +85 -0
  105. package/dist/render/surfaceTexture.d.ts +19 -0
  106. package/dist/render/surfaceTexture.js +69 -0
  107. package/dist/render/temporalAa.d.ts +14 -0
  108. package/dist/render/temporalAa.js +20 -0
  109. package/dist/render/vertexDefaults.d.ts +17 -0
  110. package/dist/render/vertexDefaults.js +24 -0
  111. package/package.json +6 -6
  112. package/src/index.ts +16 -0
  113. package/src/render/atmosphere.ts +22 -1
  114. package/src/render/backend/api.ts +9 -0
  115. package/src/render/backend/createRenderer.ts +18 -1
  116. package/src/render/backend/webgl2/renderer.ts +357 -13
  117. package/src/render/backend/webgpu/device.ts +42 -3
  118. package/src/render/backend/webgpu/flatPass.ts +63 -26
  119. package/src/render/backend/webgpu/postPass.ts +18 -0
  120. package/src/render/backend/webgpu/reflectionSurfacePass.ts +114 -0
  121. package/src/render/backend/webgpu/renderer.ts +735 -83
  122. package/src/render/displayRange.ts +61 -0
  123. package/src/render/filmicCurve.ts +229 -0
  124. package/src/render/fog.ts +19 -3
  125. package/src/render/frame/resources.ts +6 -0
  126. package/src/render/frameReflections.ts +72 -0
  127. package/src/render/pass.ts +53 -1
  128. package/src/render/reflectionSurfaceTargets.ts +105 -0
  129. package/src/render/renderQuality.ts +44 -8
  130. package/src/render/sceneTarget.ts +30 -2
  131. package/src/render/screenSpaceReflection.ts +5 -0
  132. package/src/render/shaders/filmLook.ts +2 -1
  133. package/src/render/shaders/flat/glassTint.ts +5 -1
  134. package/src/render/shaders/flat/index.ts +16 -0
  135. package/src/render/shaders/flat/main.ts +28 -0
  136. package/src/render/shaders/flat/overlay.ts +117 -0
  137. package/src/render/shaders/flat/pointShadow.ts +1 -1
  138. package/src/render/shaders/flat/reflectionSurface.ts +61 -0
  139. package/src/render/shaders/fog.ts +9 -2
  140. package/src/render/shaders/fringe.ts +59 -0
  141. package/src/render/shaders/generated/arcane.wgsl.ts +1 -1
  142. package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
  143. package/src/render/shaders/generated/caustics.wgsl.ts +1 -1
  144. package/src/render/shaders/generated/film.wgsl.ts +1 -1
  145. package/src/render/shaders/generated/fire.wgsl.ts +1 -1
  146. package/src/render/shaders/generated/flat.wgsl.ts +214 -54
  147. package/src/render/shaders/generated/line.wgsl.ts +1 -1
  148. package/src/render/shaders/generated/particle.wgsl.ts +4 -4
  149. package/src/render/shaders/generated/rush.wgsl.ts +56 -26
  150. package/src/render/shaders/generated/scatter.wgsl.ts +1 -1
  151. package/src/render/shaders/generated/smoke.wgsl.ts +1 -1
  152. package/src/render/shaders/generated/ssrResolve.wgsl.ts +30 -0
  153. package/src/render/shaders/generated/ssrTrace.wgsl.ts +22 -2
  154. package/src/render/shaders/generated/water.wgsl.ts +1 -1
  155. package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
  156. package/src/render/shaders/rush.ts +54 -11
  157. package/src/render/shaders/ssrResolve.ts +52 -0
  158. package/src/render/shaders/ssrTrace.ts +26 -0
  159. package/src/render/ssrPass.ts +138 -1
  160. package/src/render/surfaceOverlay.ts +195 -0
  161. package/src/render/surfaceTexture.ts +67 -0
  162. package/src/render/temporalAa.ts +20 -0
  163. package/src/render/vertexDefaults.ts +28 -0
@@ -4,11 +4,17 @@ import { FOG_OFF, FOG_RECEDE, fogModeOf } from '../drawFog.js';
4
4
  import { blendCovering, blendKeeping } from '../../sceneCoverage.js';
5
5
  import { mat4 } from 'gl-matrix';
6
6
  import { GL_DEPTH_REMAP, MAX_DEPTH_LAYER, depthClearFor, depthOffsetForLayer, GL_SHADOW_REMAP, REVERSED_DEPTH, SHADOW_DEPTH_CLEAR, glDepthFuncEqual, glShadowDepthFunc, } from '../../depthConvention.js';
7
+ import { maskOf } from '../../frame/resources.js';
8
+ /** The two copies a registered pass may declare it reads. See `drawPass`. */
9
+ const COLOR_SNAPSHOT = maskOf('colorSnapshot');
10
+ const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
7
11
  import { resolveGlass } from '../../glass.js';
8
12
  import { PickableSet } from '../../pickable.js';
9
13
  import { TEMPORAL_HISTORY_BLEND, TemporalHistory, jitterOffset, jitterProjection, } from '../../temporalAa.js';
10
14
  import { DecalPass } from '../../decalPass.js';
11
15
  import { SsrPass } from '../../ssrPass.js';
16
+ import { ReflectionSurfaceTargets } from '../../reflectionSurfaceTargets.js';
17
+ import { frameReflectionsRefused, resolveFrameReflections, } from '../../frameReflections.js';
12
18
  import { GlobalMediumPass } from '../../globalMediumPass.js';
13
19
  import { DEFAULT_GLOBAL_MEDIUM, WEAK_GPU_MEDIUM_STEPS, mediumActive, resolveGlobalMedium, } from '../../globalMedium.js';
14
20
  import { REFLECTION_EDGE_FADE, ReflectionQueue } from '../../screenSpaceReflection.js';
@@ -28,7 +34,8 @@ import { attachContextLoss } from '../../contextLoss.js';
28
34
  import { isWeakGpuFamily, webgl2RendererName } from '../../gpuCapability.js';
29
35
  import { GpuTimer } from '../../gpuTimer.js';
30
36
  import { compileProgram, uniformLocations } from '../../shader.js';
31
- import { FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
37
+ import { countUniformVectors, FULL_LIGHT_BUDGET, nextLightBudget, planLightBudget, } from '../../uniformVectorBudget.js';
38
+ import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
32
39
  import { FILM_FRAG, FILM_VERT } from '../../shaders/film.js';
33
40
  import { SceneTarget } from '../../sceneTarget.js';
34
41
  import { FILM_LOOK_WITHOUT_COMPOSITE } from '../../shaders/filmLook.js';
@@ -104,8 +111,9 @@ import { SCATTER_DEPTH_VERT, SCATTER_FRAG, SCATTER_VERT } from '../../shaders/sc
104
111
  import { FlockRenderer } from '../../flockRenderer.js';
105
112
  import { TextRenderer } from '../../textRenderer.js';
106
113
  import { SdfTextRenderer } from '../../sdfTextRenderer.js';
107
- import { MOTION_BLUR_MAX_UV, RUSH_REACH_UV } from '../../vertexDefaults.js';
108
- import { OUTPUT_TRANSFORM_CODE } from '../../vertexDefaults.js';
114
+ import { MOTION_BLUR_MAX_UV, OBJECT_BLUR_UNKEPT, RUSH_REACH_UV, clampMotionShare, } from '../../vertexDefaults.js';
115
+ import { OUTPUT_TRANSFORM_CODE, forwardTransformCode } from '../../vertexDefaults.js';
116
+ import { DEFAULT_FILMIC_CURVE, FILMIC_CLAMPED, FILMIC_CONSTANTS, FILMIC_WITHOUT_COMPOSITE, resolveFilmicCurve, } from '../../filmicCurve.js';
109
117
  import { deviceSnappedCellSize } from '../../textLayout.js';
110
118
  import { WindStreakRenderer } from '../../windStreakRenderer.js';
111
119
  import { InstancedBatch } from './instanced.js';
@@ -604,6 +612,10 @@ export class WebGL2Renderer {
604
612
  reflectionQueue = new ReflectionQueue();
605
613
  /** Built on the first frame that submits a surface, and never for a scene with none. */
606
614
  ssr = null;
615
+ /** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
616
+ frameReflections;
617
+ /** Their two maps on the scene's framebuffer, null where they cannot be drawn. */
618
+ reflectionTargets;
607
619
  /** Said once where a reflection cannot be traced at all. */
608
620
  reflectionsRefused = false;
609
621
  /** Where the frame's camera stands, which is what gives a reconstructed normal its sign. */
@@ -674,6 +686,8 @@ export class WebGL2Renderer {
674
686
  * exactly as it asked to be and nothing about it changes.
675
687
  */
676
688
  exposure;
689
+ /** The `filmic` curve's eight constants, held until `setFilmicCurve` is called again. */
690
+ filmConstants = new Float32Array(FILMIC_CONSTANTS);
677
691
  /**
678
692
  * This frame's veil colour, and how much of it to composite. See `setFrameVeil`.
679
693
  *
@@ -875,6 +889,15 @@ export class WebGL2Renderer {
875
889
  warnedNoPointShadowArray = false;
876
890
  /** Said once, for the retired ambient dial. */
877
891
  warnedEnvironmentAmbient = false;
892
+ /** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
893
+ overlayFloats = new Float32Array(OVERLAY_FLOATS);
894
+ /** Its atlas, and the 2D copy of it bound where the refraction copy goes. */
895
+ overlayMaps = null;
896
+ overlayTexture = null;
897
+ /** Whether this device's lit stage has room for the overlay's vectors: asked once. */
898
+ overlayRoom = null;
899
+ warnedOverlayRoom = false;
900
+ warnedOverlayBlocks = false;
878
901
  /** Whether a probe is being baked, so the passes that would recurse into one can tell. */
879
902
  probePassActive = false;
880
903
  /**
@@ -1503,6 +1526,8 @@ export class WebGL2Renderer {
1503
1526
  * call. `init` runs now rather than at the first frame, because building a pipeline in the
1504
1527
  * frame loop is the allocation the house rules are about.
1505
1528
  */
1529
+ /** Each registered pass's declared reads, as a mask, resolved once at registration. */
1530
+ passReads = new Map();
1506
1531
  registerPass(definition) {
1507
1532
  const handle = registerIn(this.passes, definition);
1508
1533
  /*
@@ -1522,6 +1547,8 @@ export class WebGL2Renderer {
1522
1547
  };
1523
1548
  if (definition.prepare !== undefined)
1524
1549
  this.preparingPasses++;
1550
+ /* Resolved once: `maskOf` builds an array, and the frame path may not. */
1551
+ this.passReads.set(handle, maskOf(...(definition.reads ?? [])));
1525
1552
  definition.init?.(this.passDevice);
1526
1553
  return handle;
1527
1554
  }
@@ -1573,10 +1600,28 @@ export class WebGL2Renderer {
1573
1600
  gl: this.gl,
1574
1601
  outputTransform: 0,
1575
1602
  outputExposure: 1,
1603
+ sceneColor: null,
1604
+ sceneDepth: null,
1576
1605
  };
1577
1606
  const context = this.passContext;
1578
1607
  context.outputTransform = this.gradeCode();
1579
1608
  context.outputExposure = this.gradeExposure();
1609
+ /*
1610
+ * The copies a pass declared it reads, taken here with everything drawn before it: colour
1611
+ * afresh at each reading pass, as WebGPU's `takePassColour` does, so a full-screen effect drawn
1612
+ * last reads the whole frame; depth afresh too, since a blit is the whole cost of either here.
1613
+ * Not in a mirror, a probe's face or a capture, whose pictures are not the frame's.
1614
+ */
1615
+ const reads = this.passReads.get(handle) ?? 0;
1616
+ const own = !this.reflectionPassActive && !this.probePassActive && !this.capturePassActive;
1617
+ context.sceneColor =
1618
+ own && (reads & COLOR_SNAPSHOT) !== 0
1619
+ ? (this.sceneTarget?.snapshotColor(true) ?? null)
1620
+ : null;
1621
+ context.sceneDepth =
1622
+ own && (reads & DEPTH_SNAPSHOT) !== 0
1623
+ ? (this.sceneTarget?.snapshotDepth(true) ?? null)
1624
+ : null;
1580
1625
  definition.draw(context);
1581
1626
  }
1582
1627
  /** Let go of a pass. A handle kept past this draws nothing; see `PassHandle`. */
@@ -1584,6 +1629,7 @@ export class WebGL2Renderer {
1584
1629
  const definition = unregisterIn(this.passes, handle);
1585
1630
  if (definition?.prepare !== undefined)
1586
1631
  this.preparingPasses--;
1632
+ this.passReads.delete(handle);
1587
1633
  if (definition !== undefined && this.passDevice !== null)
1588
1634
  definition.dispose?.(this.passDevice);
1589
1635
  }
@@ -1734,10 +1780,10 @@ export class WebGL2Renderer {
1734
1780
  gl.bindTexture(gl.TEXTURE_2D, this.clusterTexture);
1735
1781
  gl.uniform1i(u['uClusterTable'] ?? null, CLUSTER_TABLE_TEXTURE_UNIT);
1736
1782
  gl.uniform1i(u['uClustered'] ?? null, on ? 1 : 0);
1737
- if (!on)
1738
- return;
1739
1783
  /*
1740
- * The frustum, read off the projection rather than taken as a parameter.
1784
+ * The frustum, read off the projection rather than taken as a parameter — **and with the view,
1785
+ * written whether or not the lights are clustered**: a surface overlay's rim finds its place on
1786
+ * the screen through the same two (`overlayScreen`).
1741
1787
  *
1742
1788
  * A perspective matrix carries `1 / tan(fovY/2)` at [5] and that over the aspect at [0], and
1743
1789
  * the near and far planes in [14] and [10]. Deriving them here means the binner and the shader
@@ -1755,6 +1801,8 @@ export class WebGL2Renderer {
1755
1801
  this.clusterFrustum[3] = aspect;
1756
1802
  gl.uniform4fv(u['uClusterFrustum'] ?? null, this.clusterFrustum);
1757
1803
  gl.uniformMatrix4fv(u['uView'] ?? null, false, camera.view);
1804
+ if (!on)
1805
+ return;
1758
1806
  this.clusterTable ??= createClusterTable();
1759
1807
  /* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
1760
1808
  const lights = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
@@ -1828,6 +1876,11 @@ export class WebGL2Renderer {
1828
1876
  this.quality = resolveRenderQuality(qualityOptions);
1829
1877
  /* The grade this world asked for, until a frame says otherwise. See `setOutputExposure`. */
1830
1878
  this.exposure = this.quality.outputExposure;
1879
+ resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
1880
+ if (this.quality.outputTransform === 'filmic' &&
1881
+ !(this.quality.screenEffects && this.quality.hdrScene)) {
1882
+ console.warn(FILMIC_WITHOUT_COMPOSITE);
1883
+ }
1831
1884
  /*
1832
1885
  * Multisampling, but only when anything can use it.
1833
1886
  *
@@ -2086,6 +2139,29 @@ export class WebGL2Renderer {
2086
2139
  this.sceneTarget = this.quality.screenEffects
2087
2140
  ? new SceneTarget(gl, this.quality.sceneSamples, this.quality.hdrScene, this.quality.discardResolvedAttachments)
2088
2141
  : null;
2142
+ /*
2143
+ * The frame's materials' reflections, where the frame can carry them: scene light to swap the
2144
+ * probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
2145
+ * where it cannot, in the words the other backend uses. See `frameReflections.ts`.
2146
+ */
2147
+ this.frameReflections = resolveFrameReflections(this.quality.screenSpaceReflections);
2148
+ const reflectionsRefusal = this.frameReflections === null
2149
+ ? null
2150
+ : this.sceneTarget === null || !this.sceneTarget.keepsRange
2151
+ ? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
2152
+ : this.sceneTarget.sampleCount > 1
2153
+ ? 'a multisampled frame has no single depth or colour to march through'
2154
+ : null;
2155
+ if (reflectionsRefusal !== null) {
2156
+ console.warn(`WebGL2: ${frameReflectionsRefused(reflectionsRefusal)}`);
2157
+ }
2158
+ this.reflectionTargets =
2159
+ this.frameReflections !== null && reflectionsRefusal === null
2160
+ ? new ReflectionSurfaceTargets(gl)
2161
+ : null;
2162
+ /* The lit programs write the maps from the first frame they are rebuilt in. See `litOn`. */
2163
+ if (this.reflectionTargets !== null)
2164
+ this.askLit('REFLECTION_MAPS');
2089
2165
  /* Screen-space skin needs the composite to add into; without one it is pre-integrated, said once. */
2090
2166
  if (this.quality.skinScattering === 'screen-space' && this.sceneTarget === null) {
2091
2167
  console.warn("WebGL2: skinScattering 'screen-space' needs screenEffects to add into; skin is pre-integrated");
@@ -2643,10 +2719,15 @@ export class WebGL2Renderer {
2643
2719
  if (this.contextLost)
2644
2720
  return;
2645
2721
  texture.update(this.gl, source);
2722
+ /* New pixels are a new copy for the overlay's slot: see `SurfaceTexture.flatLayer`. */
2723
+ if (texture === this.overlayMaps)
2724
+ this.bindOverlayMaps(texture);
2646
2725
  }
2647
2726
  disposeSurfaceTexture(texture) {
2648
2727
  if (this.contextLost)
2649
2728
  return;
2729
+ if (texture === this.overlayMaps)
2730
+ this.bindOverlayMaps(null);
2650
2731
  texture.dispose(this.gl);
2651
2732
  this.captureTargets.get(texture)?.dispose(this.gl);
2652
2733
  this.captureTargets.delete(texture);
@@ -2981,6 +3062,8 @@ export class WebGL2Renderer {
2981
3062
  if (material?.physicalSpecular === true)
2982
3063
  this.askLit('PHYSICAL_SPECULAR');
2983
3064
  gl.uniform4fv(u['uModelParams'] ?? null, this.modelParams);
3065
+ /* The draw's overlay, which a program born mid-pass must wear too: see `setSurfaceOverlay`. */
3066
+ gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
2984
3067
  /* The albedo's effects table, or the stand-in the shader reads as none. Before the early
2985
3068
  return, so a material with no albedo still leaves a complete texture on the unit. */
2986
3069
  if (albedo === null) {
@@ -3078,6 +3161,67 @@ export class WebGL2Renderer {
3078
3161
  this.environmentDials[1] = Math.min(1, Math.max(0, amount));
3079
3162
  this.writeFlatVector('uEnvironmentDials', this.environmentDials);
3080
3163
  }
3164
+ /**
3165
+ * The overlay the following draws wear — a rim of light, a dissolve, wrinkles by region — or null
3166
+ * for none. Pass state, as the material is: `bindMeshPass` puts it back to none. See
3167
+ * `surfaceOverlay.ts` for what it lays and `shaders/flat/overlay.ts` for how.
3168
+ *
3169
+ * **Refused, said once, where the lit stage has no room for it.** Fifteen vectors more than the
3170
+ * program this device fits: counted from the source as `fitFlatProgram` counts, and on a device
3171
+ * whose limit is the 256 an Adreno 740 reports, the eight-light build at 254 has none. The draws
3172
+ * then wear nothing, which is the picture a game without the feature draws. Its atlas is bound as
3173
+ * a 2D copy of its first layer, in the refraction copy's unit (`SurfaceTexture.flatLayer`); a
3174
+ * compressed atlas has no copy and is read as none, also said once.
3175
+ */
3176
+ setSurfaceOverlay(overlay) {
3177
+ if (this.contextLost)
3178
+ return;
3179
+ let wanted = overlay;
3180
+ if (overlayLays(wanted) && !this.overlayFits()) {
3181
+ if (!this.warnedOverlayRoom) {
3182
+ this.warnedOverlayRoom = true;
3183
+ console.warn('Renderer: setSurfaceOverlay is refused on this device. The overlay takes fifteen ' +
3184
+ 'fragment uniform vectors and the lit shader already uses all but ' +
3185
+ `${this.overlayRoomLeft()} of the ${this.fragmentVectorLimit()} this GPU offers; ` +
3186
+ 'draws wear no overlay. A lower `maxLights` makes the room, or WebGPU has it.');
3187
+ }
3188
+ wanted = null;
3189
+ }
3190
+ packSurfaceOverlay(wanted, this.overlayFloats);
3191
+ if (overlayLays(wanted))
3192
+ this.askLit('SURFACE_OVERLAY');
3193
+ this.materials.dirty();
3194
+ this.writeFlatVector('uOverlay', this.overlayFloats);
3195
+ this.bindOverlayMaps((wanted?.maps ?? null));
3196
+ }
3197
+ /** The overlay's atlas into the refraction copy's unit, as a 2D copy, or the empty texture. */
3198
+ bindOverlayMaps(maps) {
3199
+ const { gl } = this;
3200
+ this.overlayMaps = maps;
3201
+ this.overlayTexture = maps === null ? null : maps.flatLayer(gl);
3202
+ if (maps !== null && this.overlayTexture === null && !this.warnedOverlayBlocks) {
3203
+ this.warnedOverlayBlocks = true;
3204
+ console.warn("Renderer: a surface overlay's maps are a compressed texture, which WebGL2 cannot copy " +
3205
+ 'into the 2D image the lit stage reads them from. The overlay is drawn without its ' +
3206
+ 'images; upload the atlas from an image instead.');
3207
+ }
3208
+ gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
3209
+ gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
3210
+ }
3211
+ /** The most fragment uniform vectors this GPU offers a stage. */
3212
+ fragmentVectorLimit() {
3213
+ return this.gl.getParameter(this.gl.MAX_FRAGMENT_UNIFORM_VECTORS);
3214
+ }
3215
+ /** What the lit shader at this renderer's budget leaves of the limit, with no overlay. */
3216
+ overlayRoomLeft() {
3217
+ return (this.fragmentVectorLimit() - countUniformVectors(this.flatFragAt(this.lightBudget, false)));
3218
+ }
3219
+ /** Whether the lit shader with the overlay compiled in fits this GPU: counted once. */
3220
+ overlayFits() {
3221
+ this.overlayRoom ??=
3222
+ countUniformVectors(this.flatFragAt(this.lightBudget, true)) <= this.fragmentVectorLimit();
3223
+ return this.overlayRoom;
3224
+ }
3081
3225
  /**
3082
3226
  * How bright the environment the surfaces drawn next reflect is. 1 is the default and identity.
3083
3227
  *
@@ -3496,10 +3640,15 @@ export class WebGL2Renderer {
3496
3640
  gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
3497
3641
  if (ditheredBlend)
3498
3642
  gl.uniform4fv(u['uCutout'] ?? null, this.cutoutUniform(this.currentMaterial, true));
3643
+ const keeps = !blend && this.reflectionKeeps();
3644
+ if (keeps)
3645
+ this.reflectionTargets?.on();
3499
3646
  if (culling && cullsInstances(count, mesh.indexCount))
3500
3647
  batch.drawCulled(gl, data, this.frustum);
3501
3648
  else
3502
3649
  batch.draw(gl, count);
3650
+ if (keeps)
3651
+ this.reflectionTargets?.off();
3503
3652
  if (coverage)
3504
3653
  gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
3505
3654
  if (ditheredBlend)
@@ -3570,7 +3719,8 @@ export class WebGL2Renderer {
3570
3719
  const { gl } = this;
3571
3720
  gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
3572
3721
  gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
3573
- gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
3722
+ /* The overlay's atlas, where one is held: it shares the unit. See `setSurfaceOverlay`. */
3723
+ gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
3574
3724
  }
3575
3725
  disposeMesh(mesh) {
3576
3726
  /*
@@ -3781,7 +3931,7 @@ export class WebGL2Renderer {
3781
3931
  */
3782
3932
  gradeCode() {
3783
3933
  return this.sceneTarget === null || !this.quality.hdrScene
3784
- ? OUTPUT_TRANSFORM_CODE[this.quality.outputTransform]
3934
+ ? forwardTransformCode(this.quality.outputTransform)
3785
3935
  : 0;
3786
3936
  }
3787
3937
  gradeExposure() {
@@ -4784,7 +4934,7 @@ export class WebGL2Renderer {
4784
4934
  * throws a sentence with the device's numbers in it rather than a bare driver string.
4785
4935
  */
4786
4936
  /** The lit fragment source this renderer's profile compiles, at a light budget. */
4787
- flatFragAt(budget) {
4937
+ flatFragAt(budget, overlay = this.litOn.SURFACE_OVERLAY) {
4788
4938
  return flatFrag({
4789
4939
  pointShadows: this.quality.pointShadows,
4790
4940
  directionalShadows: this.quality.directionalShadows,
@@ -4797,6 +4947,8 @@ export class WebGL2Renderer {
4797
4947
  surfaceEffects: this.litOn.SURFACE_EFFECTS,
4798
4948
  driftLight: this.litOn.DRIFT_LIGHT,
4799
4949
  physicalSpecular: this.litOn.PHYSICAL_SPECULAR,
4950
+ surfaceOverlay: overlay,
4951
+ reflectionMaps: this.litOn.REFLECTION_MAPS,
4800
4952
  maxLights: budget.maxLights,
4801
4953
  maxAreaLights: budget.maxAreaLights,
4802
4954
  });
@@ -5468,10 +5620,20 @@ export class WebGL2Renderer {
5468
5620
  * Set before `beginFrame` and held until changed, like `setSpeedRush`, which it sits
5469
5621
  * beside for the same reason: both describe how the finished image is presented rather
5470
5622
  * than anything in the world.
5623
+ *
5624
+ * `maxShare` is the longest smear, as a share of the frame (0.03 unless given, at most half): a
5625
+ * fast turn softens the frame rather than erasing it. **Each drawn object's own motion is
5626
+ * followed only under a reconstruction on WebGPU**, which draws the motion target it reads; here,
5627
+ * and on WebGPU without one, the camera's motion alone smears the frame, and a draw that names
5628
+ * where it was last frame is told so once.
5471
5629
  */
5472
- setCameraMotionBlur(scale) {
5630
+ setCameraMotionBlur(scale, maxShare = MOTION_BLUR_MAX_UV) {
5473
5631
  this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
5632
+ this.motionBlurMax = clampMotionShare(maxShare);
5474
5633
  }
5634
+ /** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
5635
+ motionBlurMax = MOTION_BLUR_MAX_UV;
5636
+ warnedObjectBlur = false;
5475
5637
  /**
5476
5638
  * The camera has cut: this frame is a new shot, not the next moment of the last one.
5477
5639
  *
@@ -5671,6 +5833,42 @@ export class WebGL2Renderer {
5671
5833
  ? Math.max(exposure, 1e-3)
5672
5834
  : this.quality.outputExposure;
5673
5835
  }
5836
+ /**
5837
+ * The `filmic` curve this and every later frame is graded through, until it is set again: a
5838
+ * grading volume's five numbers, solved once here into the constants the resolve reads. Ignored
5839
+ * unless `outputTransform` is `filmic`. A number out of range is clamped into it and said once,
5840
+ * since a frame loop must not throw. See `filmicCurve.ts`.
5841
+ */
5842
+ setFilmicCurve(curve) {
5843
+ if (resolveFilmicCurve(curve, this.filmConstants) && !this.warnedFilmClamped) {
5844
+ this.warnedFilmClamped = true;
5845
+ console.warn(FILMIC_CLAMPED);
5846
+ }
5847
+ }
5848
+ warnedFilmClamped = false;
5849
+ /**
5850
+ * The range the frame goes out in: `'high'` where the profile asked for `highDynamicRange`, the
5851
+ * frame holds light past white and the display shows it — on WebGPU — and `'standard'` otherwise.
5852
+ * **Always standard here**: this backend draws into a canvas of eight bits a channel, which has
5853
+ * nothing above white to show. Typed as the question rather than this backend's answer, because
5854
+ * the shared surface is derived from this class.
5855
+ */
5856
+ displayRange = 'standard';
5857
+ /** Why it is that range, in words, for a game choosing its look and for a bug report. */
5858
+ get displayRangeReason() {
5859
+ return this.quality.highDynamicRange
5860
+ ? 'WebGL2 draws into an eight-bit canvas, which has no range above white'
5861
+ : 'highDynamicRange was not asked for';
5862
+ }
5863
+ /**
5864
+ * How bright paper white and the display's peak are, in one unit, held until changed: their
5865
+ * ratio is how far above white the highlights may run where `displayRange` is `'high'`, and
5866
+ * nothing changes where it is standard, which is always here.
5867
+ */
5868
+ setDisplayLuminance(paperWhite, peak) {
5869
+ void paperWhite;
5870
+ void peak;
5871
+ }
5674
5872
  /**
5675
5873
  * Composite a flat colour over the finished frame — for a cut dipping to white or to black.
5676
5874
  *
@@ -5757,6 +5955,20 @@ export class WebGL2Renderer {
5757
5955
  }
5758
5956
  this.sceneTarget.setVignette(strength);
5759
5957
  }
5958
+ /**
5959
+ * The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
5960
+ * and green are pulled toward the centre by their wavelength's distance from blue, growing from
5961
+ * `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
5962
+ * scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
5963
+ * doing nothing. See `shaders/fringe.ts`.
5964
+ */
5965
+ setChromaticAberration(intensity, start = 0) {
5966
+ if (this.sceneTarget === null) {
5967
+ this.warnLookWithoutComposite(intensity);
5968
+ return;
5969
+ }
5970
+ this.sceneTarget.setChromaticAberration(intensity, start);
5971
+ }
5760
5972
  /**
5761
5973
  * Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
5762
5974
  * grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
@@ -5937,6 +6149,8 @@ export class WebGL2Renderer {
5937
6149
  beginFrame(clearColor) {
5938
6150
  if (this.contextLost)
5939
6151
  return;
6152
+ /* One frame of the jitter's sequence however many times the frame binds its camera. */
6153
+ this.temporalHistory.nextFrame();
5940
6154
  this.skinScattered = false;
5941
6155
  this.skinScatter?.reset();
5942
6156
  if (this.litAsked) {
@@ -6003,6 +6217,8 @@ export class WebGL2Renderer {
6003
6217
  }
6004
6218
  }
6005
6219
  this.sceneTarget?.begin(this.canvas.width, this.canvas.height);
6220
+ /* The materials' maps on the scene's framebuffer, cleared and disabled: `reflectionKeeps`. */
6221
+ this.reflectionTargets?.begin(this.canvas.width, this.canvas.height);
6006
6222
  gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
6007
6223
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
6008
6224
  }
@@ -6052,7 +6268,7 @@ export class WebGL2Renderer {
6052
6268
  motion = {
6053
6269
  reprojection: this.reprojection,
6054
6270
  strength: blur,
6055
- max: MOTION_BLUR_MAX_UV,
6271
+ max: this.motionBlurMax,
6056
6272
  };
6057
6273
  }
6058
6274
  }
@@ -6132,7 +6348,11 @@ export class WebGL2Renderer {
6132
6348
  : undefined;
6133
6349
  /* The grade travels to the resolve only where the mesh pass gave it up. See `bindMeshPass`. */
6134
6350
  const lateGrade = this.quality.hdrScene
6135
- ? { transform: OUTPUT_TRANSFORM_CODE[this.quality.outputTransform], exposure: this.exposure }
6351
+ ? {
6352
+ transform: OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0,
6353
+ exposure: this.exposure,
6354
+ film: this.filmConstants,
6355
+ }
6136
6356
  : { transform: 0, exposure: 1 };
6137
6357
  /*
6138
6358
  * Depth of field, on the same ceiling-and-dial split as bloom above: `depthOfField` decides
@@ -6194,6 +6414,30 @@ export class WebGL2Renderer {
6194
6414
  * drawn over a reflective floor rather than under it, and before the post chain so bloom and
6195
6415
  * the grade see a reflection exactly as they would see any other part of the picture.
6196
6416
  */
6417
+ /*
6418
+ * **The frame's materials' reflections first**, on the same terms: every pixel the opaque lit
6419
+ * draws left a reflection for, marched and swapped for the probe's share. The maps leave the
6420
+ * scene's framebuffer before the resolve draws into it while sampling them.
6421
+ */
6422
+ const reflectionTargets = this.reflectionTargets;
6423
+ const probeMap = reflectionTargets?.probeTexture ?? null;
6424
+ const tintMap = reflectionTargets?.tintTexture ?? null;
6425
+ if (reflectionTargets !== null &&
6426
+ this.frameReflections !== null &&
6427
+ probeMap !== null &&
6428
+ tintMap !== null &&
6429
+ this.frameViewProj !== null &&
6430
+ this.sceneTarget !== null) {
6431
+ const scene = this.sceneTarget.colorAttachment();
6432
+ const depth = this.sceneTarget.snapshotDepth(true);
6433
+ if (scene !== null && depth !== null) {
6434
+ this.ssr ??= new SsrPass(this.gl, this.sceneTarget.keepsRange);
6435
+ this.ssr.traceMaterials(scene, depth, probeMap, tintMap, this.frameViewProj, this.frameEye, this.canvas.width, this.canvas.height, REFLECTION_EDGE_FADE, this.frameReflections);
6436
+ this.sceneTarget.bind();
6437
+ reflectionTargets.detach();
6438
+ this.ssr.resolveMaterials(probeMap, tintMap, this.canvas.width, this.canvas.height, this.frameReflections);
6439
+ }
6440
+ }
6197
6441
  if (this.reflectionQueue.length > 0 && this.frameViewProj !== null) {
6198
6442
  const scene = this.sceneTarget?.colorAttachment() ?? null;
6199
6443
  const depth = this.sceneTarget?.snapshotDepth(true) ?? null;
@@ -6531,6 +6775,10 @@ export class WebGL2Renderer {
6531
6775
  this.materialHasMaps = false;
6532
6776
  this.currentMaterial = null;
6533
6777
  this.currentSurfaceTexture = null;
6778
+ /* Nor an overlay: the atlas's unit is given the empty texture with the refraction copy's. */
6779
+ this.overlayFloats.fill(0);
6780
+ this.overlayMaps = null;
6781
+ this.overlayTexture = null;
6534
6782
  /* Nor an ambient of its own: the frame's, until a draw asks. See `setAmbientSH`. */
6535
6783
  this.ambientSH.fill(0);
6536
6784
  /* No reflection and an environment at its own brightness; the emission the world asks for. */
@@ -6656,8 +6904,10 @@ export class WebGL2Renderer {
6656
6904
  * unless a draw sets the strength above zero.
6657
6905
  */
6658
6906
  gl.activeTexture(gl.TEXTURE0 + REFRACT_SCENE_TEXTURE_UNIT);
6659
- gl.bindTexture(gl.TEXTURE_2D, this.emptyTexture2D);
6907
+ gl.bindTexture(gl.TEXTURE_2D, this.overlayTexture ?? this.emptyTexture2D);
6660
6908
  gl.uniform1i(u['uRefractScene'] ?? null, REFRACT_SCENE_TEXTURE_UNIT);
6909
+ /* The draws' overlay, none at a pass's start, and its atlas above: see `setSurfaceOverlay`. */
6910
+ gl.uniform4fv(u['uOverlay'] ?? null, this.overlayFloats);
6661
6911
  /* Neither refracting nor glass, which is every draw that does not say otherwise. See
6662
6912
  `bindSeeThrough` for the four numbers in the one vector. */
6663
6913
  gl.uniform4f(u['uSeeThrough'] ?? null, 0, 0, 0, 0);
@@ -7036,9 +7286,16 @@ export class WebGL2Renderer {
7036
7286
  * here, so there is no motion target for this to be written into, and a mover passed here is
7037
7287
  * neither read nor advanced.
7038
7288
  */
7039
- _previousModel = null) {
7289
+ previousModel = null) {
7040
7290
  if (this.contextLost)
7041
7291
  return;
7292
+ /* A mover's own motion is not blurred here: see `setCameraMotionBlur`. Said once. */
7293
+ if (previousModel !== null &&
7294
+ !this.warnedObjectBlur &&
7295
+ this.quality.cameraMotionBlur * this.motionBlurScale > 0) {
7296
+ this.warnedObjectBlur = true;
7297
+ console.warn(OBJECT_BLUR_UNKEPT);
7298
+ }
7042
7299
  /* Geometry that has not all arrived is not drawn, nor a mesh with none. See `Mesh.complete`
7043
7300
  and `Mesh.indexCount`. */
7044
7301
  if (!mesh.complete || mesh.indexCount === 0)
@@ -7071,7 +7328,13 @@ export class WebGL2Renderer {
7071
7328
  * light and its colour, each into the blur's own target.
7072
7329
  */
7073
7330
  const splits = this.skinSplits();
7331
+ /* An opaque lit draw into the frame writes its material's reflection too: see `reflectionKeeps`. */
7332
+ const keeps = this.reflectionKeeps();
7333
+ if (keeps)
7334
+ this.reflectionTargets?.on();
7074
7335
  this.drawMeshHalf(mesh, model, depthLayer, tint, splits ? 'scene' : 'whole');
7336
+ if (keeps)
7337
+ this.reflectionTargets?.off();
7075
7338
  if (splits && this.skinScatter !== null) {
7076
7339
  this.skinScatter.begin();
7077
7340
  this.drawMeshHalf(mesh, model, depthLayer, tint, 'diffuse');
@@ -7085,6 +7348,18 @@ export class WebGL2Renderer {
7085
7348
  * skin yet, this is the frame's own picture — not a mirror's or a probe's, nor an overlay after
7086
7349
  * the frame — and the blur's target can be had.
7087
7350
  */
7351
+ /**
7352
+ * Whether a draw now is the frame's own, opaque and lit, and so writes the two maps beside its
7353
+ * colour: not a mirror's, a probe's or a capture's, whose framebuffers have no maps, and not after
7354
+ * the frame. See `reflectionSurfaceTargets.ts`.
7355
+ */
7356
+ reflectionKeeps() {
7357
+ return (this.reflectionTargets !== null &&
7358
+ !this.reflectionPassActive &&
7359
+ !this.probePassActive &&
7360
+ !this.capturePassActive &&
7361
+ !this.framePresented);
7362
+ }
7088
7363
  skinSplits() {
7089
7364
  const skin = this.skinScatter;
7090
7365
  const scene = this.sceneTarget;
@@ -7111,12 +7386,18 @@ export class WebGL2Renderer {
7111
7386
  const projection = this.frameProjection;
7112
7387
  if (skin === null || scene === null || this.skinScattered)
7113
7388
  return;
7389
+ /*
7390
+ * **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
7391
+ * bake or a scene capture lands over none of the frame's skin. Latching there switched the
7392
+ * spread off for the frame, so a capture with a glowing sign in it left every face drawn after
7393
+ * it flat. `scripts/capture-check.mjs`.
7394
+ */
7395
+ if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
7396
+ return;
7114
7397
  /* Latched whether or not skin was drawn: a skin drawn after this point is the whole surface. */
7115
7398
  this.skinScattered = true;
7116
7399
  if (!skin.pending)
7117
7400
  return;
7118
- if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
7119
- return;
7120
7401
  if (projection === null)
7121
7402
  return;
7122
7403
  const depth = scene.snapshotDepth(true);
@@ -6,6 +6,7 @@
6
6
  * split matters because these two fail for unrelated reasons: a device is lost by a driver
7
7
  * reset the page did not cause, and a frame is wrong because of something the frame did.
8
8
  */
9
+ import type { DisplayRange, DisplayRangeChoice } from '../../displayRange.ts';
9
10
  /** What the renderer needs from the canvas it draws into, with the API's shape absorbed. */
10
11
  export interface GpuSurface {
11
12
  readonly device: GPUDevice;
@@ -21,6 +22,9 @@ export interface GpuSurface {
21
22
  readonly canvas: HTMLCanvasElement;
22
23
  /** The swap chain's format, which every render pipeline has to be built against. */
23
24
  readonly format: GPUTextureFormat;
25
+ /** The range the canvas was configured in, and why. See `displayRange.ts`. */
26
+ readonly displayRange: DisplayRange;
27
+ readonly displayRangeReason: string;
24
28
  /** Whether the device has gone. Read per frame; never throws. */
25
29
  readonly lost: boolean;
26
30
  /** Called when the device is lost. Fires immediately if it already has been. */
@@ -49,4 +53,6 @@ export interface GpuSurface {
49
53
  * consumer its GPU had failed every time it unmounted a canvas on purpose, so a disposed
50
54
  * surface stays silent.
51
55
  */
52
- export declare function createGpuSurface(canvas: HTMLCanvasElement, device: GPUDevice): GpuSurface;
56
+ export declare function createGpuSurface(canvas: HTMLCanvasElement, device: GPUDevice,
57
+ /** The range the profile and the display allow (`chooseDisplayRange`); standard unless given. */
58
+ wanted?: DisplayRangeChoice): GpuSurface;