@driftengine/core 4.6.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +37 -0
- package/README.md +1 -1
- package/dist/render/backend/budget.d.ts +6 -0
- package/dist/render/backend/budget.js +3 -0
- package/dist/render/backend/webgl2/renderer.js +11 -7
- package/dist/render/backend/webgpu/buffers.d.ts +7 -0
- package/dist/render/backend/webgpu/motionPass.js +2 -1
- package/dist/render/backend/webgpu/renderer.d.ts +30 -1
- package/dist/render/backend/webgpu/renderer.js +62 -55
- package/dist/render/mesh.d.ts +6 -1
- package/dist/render/mesh.js +6 -1
- package/dist/render/renderQuality.d.ts +35 -0
- package/dist/render/renderQuality.js +19 -0
- package/package.json +6 -6
- package/scripts/png.d.mts +1 -1
- package/scripts/png.mjs +99 -25
- package/src/render/backend/budget.ts +11 -1
- package/src/render/backend/webgl2/renderer.ts +11 -7
- package/src/render/backend/webgpu/buffers.ts +7 -0
- package/src/render/backend/webgpu/motionPass.ts +2 -1
- package/src/render/backend/webgpu/renderer.ts +62 -56
- package/src/render/mesh.ts +6 -1
- package/src/render/renderQuality.ts +55 -0
|
@@ -97,6 +97,37 @@ export interface RenderQuality {
|
|
|
97
97
|
* halved frame rate rather than a slightly slower one.
|
|
98
98
|
*/
|
|
99
99
|
readonly maxDrawingBufferPixels: number;
|
|
100
|
+
/**
|
|
101
|
+
* How many times a frame may change material, on a backend that keeps a slot for each.
|
|
102
|
+
*
|
|
103
|
+
* **WebGPU spends a slot of a uniform ring per material change and skips every draw past it**,
|
|
104
|
+
* because a buffer rewritten between two draws would hand both of them its last contents. So
|
|
105
|
+
* this is a ceiling on what a frame can show, not a performance hint: a scene that needs more
|
|
106
|
+
* loses whatever it asks for last — measured on a bought city of 1,139 materials, whose street
|
|
107
|
+
* spends some nine hundred and whose river's planar mirror spends the rest twice over.
|
|
108
|
+
*
|
|
109
|
+
* **What it costs is memory, paid at construction**: a slot is the fragment block twice over,
|
|
110
|
+
* staging and buffer, so 7 to 13 KB depending on the permutation built — 1,024 is 7 to 13 MB
|
|
111
|
+
* and 2,048 twice that. Hence a default that suits most scenes and an option for the one that
|
|
112
|
+
* measures past it, rather than a number every consumer pays for. WebGL2 writes each uniform
|
|
113
|
+
* into the command stream and keeps no ring, so there it is read and does nothing, and its
|
|
114
|
+
* budget line reports no ceiling. What would make it wrong is a scene that switches material
|
|
115
|
+
* every draw, which no ring answers: many copies of one mesh want one instanced draw.
|
|
116
|
+
*/
|
|
117
|
+
readonly materialChangesPerFrame: number;
|
|
118
|
+
/**
|
|
119
|
+
* How many draws a frame may make, on a backend that keeps a uniform slot for each — the main
|
|
120
|
+
* pass and the shadow passes each hold this many.
|
|
121
|
+
*
|
|
122
|
+
* **The same shape as `materialChangesPerFrame` and the same failure**: WebGPU skips every draw
|
|
123
|
+
* past the ring. A frame that draws two hundred pays for two hundred whatever this says, so
|
|
124
|
+
* raising it costs no bandwidth; it costs memory at construction, 1,540 bytes a slot — 6 MB at
|
|
125
|
+
* the default 4,096. A city whose street spends three and a half thousand and whose river
|
|
126
|
+
* mirrors that street spends more than the default, and a scene in the ordinary band of two to
|
|
127
|
+
* ten thousand draws can meet it. WebGL2 keeps no ring and reads this without effect. What would
|
|
128
|
+
* make it wrong is a frame that approaches it on the CPU first, which four thousand draws can.
|
|
129
|
+
*/
|
|
130
|
+
readonly drawsPerFrame: number;
|
|
100
131
|
readonly directionalShadows: boolean;
|
|
101
132
|
readonly pointShadows: boolean;
|
|
102
133
|
/**
|
|
@@ -821,6 +852,10 @@ export interface RenderQuality {
|
|
|
821
852
|
export type RenderQualityOptions = Partial<RenderQuality>;
|
|
822
853
|
|
|
823
854
|
export const MAX_SHADOW_FILTER_TAPS = 12;
|
|
855
|
+
/** The most material changes a frame may be built to hold. See `materialChangesPerFrame`. */
|
|
856
|
+
export const MAX_MATERIAL_CHANGES_PER_FRAME = 8192;
|
|
857
|
+
/** The most draws a frame may be built to hold. See `drawsPerFrame`. */
|
|
858
|
+
export const MAX_DRAWS_PER_FRAME = 65536;
|
|
824
859
|
export const MAX_WATER_REFLECTION_FILTER_TAPS = 9;
|
|
825
860
|
export const POINT_SHADOW_FADE_START = 0.35;
|
|
826
861
|
export const DIRECTIONAL_SHADOW_FADE_START = 0.65;
|
|
@@ -864,6 +899,8 @@ export const DEFAULT_RENDER_QUALITY: Readonly<RenderQuality> = Object.freeze({
|
|
|
864
899
|
* if it is above where people actually are. Consumers wanting the pixels set it to 0.
|
|
865
900
|
*/
|
|
866
901
|
maxDrawingBufferPixels: 12_000_000,
|
|
902
|
+
materialChangesPerFrame: 1024,
|
|
903
|
+
drawsPerFrame: 4096,
|
|
867
904
|
directionalShadows: true,
|
|
868
905
|
pointShadows: true,
|
|
869
906
|
/* The engine's own budgets, which the renderer lowers per device when it has to. */
|
|
@@ -1015,6 +1052,9 @@ export function resolveRenderQuality(options: RenderQualityOptions = {}): Readon
|
|
|
1015
1052
|
maxDevicePixelRatio: options.maxDevicePixelRatio ?? DEFAULT_RENDER_QUALITY.maxDevicePixelRatio,
|
|
1016
1053
|
maxDrawingBufferPixels:
|
|
1017
1054
|
options.maxDrawingBufferPixels ?? DEFAULT_RENDER_QUALITY.maxDrawingBufferPixels,
|
|
1055
|
+
materialChangesPerFrame:
|
|
1056
|
+
options.materialChangesPerFrame ?? DEFAULT_RENDER_QUALITY.materialChangesPerFrame,
|
|
1057
|
+
drawsPerFrame: options.drawsPerFrame ?? DEFAULT_RENDER_QUALITY.drawsPerFrame,
|
|
1018
1058
|
directionalShadows: options.directionalShadows ?? DEFAULT_RENDER_QUALITY.directionalShadows,
|
|
1019
1059
|
pointShadows: options.pointShadows ?? DEFAULT_RENDER_QUALITY.pointShadows,
|
|
1020
1060
|
/* Whole slots, at least one, never above what the shader is written for: these two are
|
|
@@ -1132,6 +1172,21 @@ export function resolveRenderQuality(options: RenderQualityOptions = {}): Readon
|
|
|
1132
1172
|
// Zero is legal and means uncapped, so this is the non-negative check rather than
|
|
1133
1173
|
// the positive one.
|
|
1134
1174
|
nonNegative('maxDrawingBufferPixels', quality.maxDrawingBufferPixels);
|
|
1175
|
+
positiveInteger('materialChangesPerFrame', quality.materialChangesPerFrame);
|
|
1176
|
+
if (quality.materialChangesPerFrame > MAX_MATERIAL_CHANGES_PER_FRAME) {
|
|
1177
|
+
throw new Error(
|
|
1178
|
+
`materialChangesPerFrame is ${quality.materialChangesPerFrame}, and at most ` +
|
|
1179
|
+
`${MAX_MATERIAL_CHANGES_PER_FRAME} are kept: past that the ring is a hundred megabytes, ` +
|
|
1180
|
+
'and a frame changing material that often wants its draws batched instead.',
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
positiveInteger('drawsPerFrame', quality.drawsPerFrame);
|
|
1184
|
+
if (quality.drawsPerFrame > MAX_DRAWS_PER_FRAME) {
|
|
1185
|
+
throw new Error(
|
|
1186
|
+
`drawsPerFrame is ${quality.drawsPerFrame}, and at most ${MAX_DRAWS_PER_FRAME} are kept: ` +
|
|
1187
|
+
'past that the rings are a hundred megabytes, and a frame drawing that often wants instancing.',
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1135
1190
|
positiveInteger('directionalShadowMapSize', quality.directionalShadowMapSize);
|
|
1136
1191
|
positiveInteger('pointShadowFaceSize', quality.pointShadowFaceSize);
|
|
1137
1192
|
positiveInteger('shadowFilterTaps', quality.shadowFilterTaps);
|