@aethervtt/shard-render 0.0.1 → 0.1.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/dist/assets.d.ts +3 -0
- package/dist/assets.js +43 -2
- package/dist/atmosphere-nodes.d.ts +9 -0
- package/dist/atmosphere-nodes.js +57 -18
- package/dist/atmosphere-plugin.js +8 -2
- package/dist/baseline/atmosphere.d.ts +16 -0
- package/dist/baseline/atmosphere.js +301 -0
- package/dist/baseline/data-texture.d.ts +36 -0
- package/dist/baseline/data-texture.js +181 -0
- package/dist/baseline/environment.d.ts +14 -0
- package/dist/baseline/environment.js +368 -0
- package/dist/baseline/exposure.d.ts +25 -0
- package/dist/baseline/exposure.js +120 -0
- package/dist/baseline/lights.d.ts +33 -0
- package/dist/baseline/lights.js +133 -0
- package/dist/baseline/textures.d.ts +97 -0
- package/dist/baseline/textures.js +203 -0
- package/dist/camera.d.ts +2 -0
- package/dist/camera.js +4 -0
- package/dist/culling-shaders.js +21 -2
- package/dist/culling.d.ts +11 -2
- package/dist/culling.js +44 -18
- package/dist/cutaway-plugin.d.ts +48 -0
- package/dist/cutaway-plugin.js +364 -0
- package/dist/cutaway.d.ts +27 -0
- package/dist/cutaway.js +49 -0
- package/dist/data-store.d.ts +69 -0
- package/dist/data-store.js +105 -0
- package/dist/debug-shaders.js +17 -3
- package/dist/debug-views.js +4 -2
- package/dist/deferred-shaders.js +2 -0
- package/dist/deferred.js +24 -3
- package/dist/deform.d.ts +5 -4
- package/dist/deform.js +4 -9
- package/dist/describe-plugin.d.ts +4 -2
- package/dist/describe-plugin.js +75 -3
- package/dist/display-nodes.js +15 -4
- package/dist/dynamic-resolution.d.ts +2 -0
- package/dist/dynamic-resolution.js +6 -1
- package/dist/environment-plugin.js +17 -2
- package/dist/environment-state.d.ts +7 -3
- package/dist/environment-state.js +20 -9
- package/dist/environment.js +7 -1
- package/dist/features.d.ts +49 -0
- package/dist/features.js +106 -0
- package/dist/forward.d.ts +38 -2
- package/dist/forward.js +582 -184
- package/dist/fxaa.js +12 -2
- package/dist/gizmos-plugin.js +7 -0
- package/dist/gizmos.d.ts +3 -3
- package/dist/gizmos.js +11 -13
- package/dist/gpu-assets.d.ts +58 -4
- package/dist/gpu-assets.js +191 -8
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +30 -5
- package/dist/health.d.ts +58 -0
- package/dist/health.js +112 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +286 -9
- package/dist/instances.d.ts +119 -8
- package/dist/instances.js +387 -78
- package/dist/interior-plugin.d.ts +121 -0
- package/dist/interior-plugin.js +465 -0
- package/dist/interior.d.ts +24 -0
- package/dist/interior.js +13 -0
- package/dist/layers.d.ts +35 -0
- package/dist/layers.js +49 -0
- package/dist/lens-plugin.d.ts +43 -0
- package/dist/lens-plugin.js +304 -0
- package/dist/lens.d.ts +41 -0
- package/dist/lens.js +99 -0
- package/dist/lights.d.ts +50 -5
- package/dist/lights.js +101 -22
- package/dist/material-pipelines.d.ts +37 -4
- package/dist/material-pipelines.js +102 -5
- package/dist/materials.d.ts +23 -0
- package/dist/materials.js +44 -1
- package/dist/outline-plugin.d.ts +48 -0
- package/dist/outline-plugin.js +529 -0
- package/dist/outline.d.ts +15 -0
- package/dist/outline.js +42 -0
- package/dist/overlays.js +1 -0
- package/dist/picking.d.ts +17 -0
- package/dist/picking.js +40 -11
- package/dist/pixel-perfect-plugin.js +10 -2
- package/dist/plugin.d.ts +58 -6
- package/dist/plugin.js +191 -24
- package/dist/post-common.d.ts +2 -0
- package/dist/post-common.js +3 -0
- package/dist/post-nodes.d.ts +2 -1
- package/dist/post-nodes.js +111 -19
- package/dist/post-plugin.js +5 -3
- package/dist/post-shaders.js +51 -6
- package/dist/projection.d.ts +24 -0
- package/dist/projection.js +87 -0
- package/dist/readback.d.ts +1 -0
- package/dist/readback.js +1 -0
- package/dist/render-scale.js +1 -0
- package/dist/screen-effects.d.ts +97 -0
- package/dist/screen-effects.js +192 -0
- package/dist/shaders.d.ts +13 -0
- package/dist/shaders.js +131 -29
- package/dist/shadow-catcher.d.ts +9 -0
- package/dist/shadow-catcher.js +133 -0
- package/dist/shadows.d.ts +25 -1
- package/dist/shadows.js +97 -19
- package/dist/standard-fields.d.ts +1 -0
- package/dist/standard-fields.js +5 -0
- package/dist/standard.js +12 -0
- package/dist/stats.d.ts +45 -1
- package/dist/stats.js +91 -3
- package/dist/surface-model.d.ts +57 -0
- package/dist/surface-model.js +289 -0
- package/dist/surface-shaders.d.ts +3 -0
- package/dist/surface-shaders.js +160 -0
- package/dist/surface.d.ts +40 -0
- package/dist/surface.js +86 -0
- package/dist/target.d.ts +1 -13
- package/dist/target.js +1 -26
- package/dist/testing.d.ts +10 -0
- package/dist/testing.js +33 -1
- package/dist/tier.d.ts +13 -0
- package/dist/tier.js +14 -0
- package/dist/timer.d.ts +2 -0
- package/dist/timer.js +7 -1
- package/dist/variants.d.ts +37 -0
- package/dist/variants.js +104 -0
- package/dist/view-visibility-plugin.d.ts +61 -0
- package/dist/view-visibility-plugin.js +267 -0
- package/dist/view-visibility.d.ts +28 -0
- package/dist/view-visibility.js +46 -0
- package/dist/view.d.ts +29 -1
- package/dist/view.js +57 -34
- package/package.json +15 -11
package/dist/fxaa.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { definePlugin } from "@aethervtt/shard-runtime";
|
|
2
|
+
import { addRenderFeatures } from "./features.js";
|
|
2
3
|
import { RenderPhase } from "./graph.js";
|
|
3
4
|
import { Graph, Shaders } from "./plugin.js";
|
|
4
5
|
import { hasEffect, PostEffect, PostFeatures } from "./post.js";
|
|
5
6
|
import { idOf, PostCache, sampler, tex } from "./post-common.js";
|
|
6
7
|
import { FXAA_SHADERS } from "./post-shaders.js";
|
|
7
8
|
import { registerShaders } from "./shaders.js";
|
|
9
|
+
import { cameraOf } from "./view.js";
|
|
8
10
|
function fxaaNode() {
|
|
9
11
|
const cache = new PostCache();
|
|
10
12
|
let layout;
|
|
@@ -24,15 +26,17 @@ function fxaaNode() {
|
|
|
24
26
|
}
|
|
25
27
|
const input = ctx.texture("ldr");
|
|
26
28
|
const format = ctx.texture("display").format;
|
|
29
|
+
const alpha = cameraOf(ctx.view)?.alphaOutput === true;
|
|
27
30
|
const pipeline = cache.render(
|
|
28
31
|
ctx,
|
|
29
|
-
`fxaa/${format}`,
|
|
32
|
+
`fxaa/${format}${alpha ? "/alpha" : ""}`,
|
|
30
33
|
"shard::post::fxaa",
|
|
31
34
|
"fs",
|
|
32
35
|
[layout],
|
|
33
36
|
[{ format }],
|
|
34
37
|
{
|
|
35
|
-
SRGB_TARGET: format.endsWith("-srgb")
|
|
38
|
+
SRGB_TARGET: format.endsWith("-srgb"),
|
|
39
|
+
TRANSPARENT: alpha
|
|
36
40
|
}
|
|
37
41
|
);
|
|
38
42
|
if (!pipeline) return;
|
|
@@ -57,6 +61,12 @@ const fxaaPlugin = definePlugin({
|
|
|
57
61
|
registerShaders(app.world.resource(Shaders), FXAA_SHADERS);
|
|
58
62
|
const graph = app.world.resource(Graph);
|
|
59
63
|
graph.declare({ name: "fxaa-in", format: "view" });
|
|
64
|
+
addRenderFeatures(app.world, {
|
|
65
|
+
name: "render/fxaa",
|
|
66
|
+
description: "Fast approximate anti-aliasing.",
|
|
67
|
+
nodes: ["post/fxaa"],
|
|
68
|
+
baseline: { strategy: "The same fragment pass" }
|
|
69
|
+
});
|
|
60
70
|
graph.addNode("post/fxaa", fxaaNode());
|
|
61
71
|
}
|
|
62
72
|
});
|
package/dist/gizmos-plugin.js
CHANGED
|
@@ -2,6 +2,7 @@ import { First, Last } from "@aethervtt/shard-core";
|
|
|
2
2
|
import { definePlugin } from "@aethervtt/shard-runtime";
|
|
3
3
|
import { OriginShift } from "@aethervtt/shard-transform";
|
|
4
4
|
import { GIZMO_SHADERS } from "./debug-shaders.js";
|
|
5
|
+
import { addRenderFeatures } from "./features.js";
|
|
5
6
|
import { upload } from "./forward.js";
|
|
6
7
|
import { beginGizmos, GizmoGpuResource, Gizmos, gizmoNode, uploadGizmos } from "./gizmos.js";
|
|
7
8
|
import { DebugOverlays, drawOverlays, gridsOverlay } from "./overlays.js";
|
|
@@ -26,6 +27,12 @@ const gizmosPlugin = definePlugin({
|
|
|
26
27
|
},
|
|
27
28
|
ready(app) {
|
|
28
29
|
registerShaders(app.world.resource(Shaders), GIZMO_SHADERS);
|
|
30
|
+
addRenderFeatures(app.world, {
|
|
31
|
+
name: "render/gizmos",
|
|
32
|
+
description: "Debug lines, shapes and labels.",
|
|
33
|
+
nodes: ["gizmos"],
|
|
34
|
+
baseline: { strategy: "Line and glyph records in data textures" }
|
|
35
|
+
});
|
|
29
36
|
app.world.resource(Graph).addNode("gizmos", gizmoNode(app.world));
|
|
30
37
|
}
|
|
31
38
|
});
|
package/dist/gizmos.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type World } from '@aethervtt/shard-core';
|
|
2
|
-
import {
|
|
2
|
+
import { DataStore } from './data-store.js';
|
|
3
3
|
import { type NodeDescriptor } from './graph.js';
|
|
4
4
|
/**
|
|
5
5
|
* Immediate-mode debug drawing. Any system calls `world.resource(Gizmos).line(…)` and the line
|
|
@@ -127,8 +127,8 @@ export declare const Gizmos: import("@aethervtt/shard-core").ResourceDef<GizmoSt
|
|
|
127
127
|
export declare const beginGizmos: import("@aethervtt/shard-core").SystemDef<undefined>;
|
|
128
128
|
interface GizmoGpu {
|
|
129
129
|
generation: number;
|
|
130
|
-
lines:
|
|
131
|
-
glyphs:
|
|
130
|
+
lines: DataStore;
|
|
131
|
+
glyphs: DataStore;
|
|
132
132
|
atlas: GPUTexture;
|
|
133
133
|
layouts: {
|
|
134
134
|
view: GPUBindGroupLayout;
|
package/dist/gizmos.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { defineResource, defineSystem, mat4 } from "@aethervtt/shard-core";
|
|
2
|
-
import { GpuBuffer } from "@aethervtt/shard-gpu";
|
|
3
2
|
import { Time } from "@aethervtt/shard-runtime";
|
|
3
|
+
import { DataStore, dataEntry } from "./data-store.js";
|
|
4
4
|
import { ForwardStateResource } from "./forward.js";
|
|
5
5
|
import { LABEL_FONT, labelAtlas, labelWidth } from "./gizmo-font.js";
|
|
6
6
|
import { RenderPhase } from "./graph.js";
|
|
7
7
|
import { Gpu, Shaders } from "./plugin.js";
|
|
8
|
+
import { depthReadEntry } from "./tier.js";
|
|
8
9
|
import { cameraOf } from "./view.js";
|
|
9
10
|
const GIZMO_LINE_FLOATS = 8;
|
|
10
11
|
const GLYPH_FLOATS = 12;
|
|
@@ -537,12 +538,9 @@ function gizmoGpu(world, gpu) {
|
|
|
537
538
|
const glyphData = new Float32Array(GLYPH_FLOATS * 256);
|
|
538
539
|
r.gpu = {
|
|
539
540
|
generation: gpu.generation,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
usage: GPUBufferUsage.STORAGE,
|
|
544
|
-
size: 4096
|
|
545
|
-
}),
|
|
541
|
+
// Read by the vertex stage: data textures on baseline (0064).
|
|
542
|
+
lines: new DataStore(gpu, { label: "gizmos/lines", size: 4096 }),
|
|
543
|
+
glyphs: new DataStore(gpu, { label: "gizmos/glyphs", size: 4096 }),
|
|
546
544
|
atlas,
|
|
547
545
|
layouts: {
|
|
548
546
|
view: gpu.layouts.bindGroupLayout({
|
|
@@ -552,9 +550,9 @@ function gizmoGpu(world, gpu) {
|
|
|
552
550
|
data: gpu.layouts.bindGroupLayout({
|
|
553
551
|
label: "gizmos/data",
|
|
554
552
|
entries: [
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
553
|
+
dataEntry(gpu, 0, V),
|
|
554
|
+
dataEntry(gpu, 1, V),
|
|
555
|
+
depthReadEntry(gpu, 2, F),
|
|
558
556
|
{ binding: 3, visibility: F, texture: { sampleType: "float" } }
|
|
559
557
|
]
|
|
560
558
|
})
|
|
@@ -733,11 +731,11 @@ function gizmoNode(world) {
|
|
|
733
731
|
gpu,
|
|
734
732
|
r,
|
|
735
733
|
`data/${ctx.view.name}`,
|
|
736
|
-
`${
|
|
734
|
+
`${r.lines.version}/${r.glyphs.version}/${idOf(depth)}/${idOf(r.atlas)}`,
|
|
737
735
|
r.layouts.data,
|
|
738
736
|
() => [
|
|
739
|
-
{ binding: 0, resource:
|
|
740
|
-
{ binding: 1, resource:
|
|
737
|
+
{ binding: 0, resource: r.lines.resource() },
|
|
738
|
+
{ binding: 1, resource: r.glyphs.resource() },
|
|
741
739
|
{ binding: 2, resource: depth.createView() },
|
|
742
740
|
{ binding: 3, resource: r.atlas.createView() }
|
|
743
741
|
]
|
package/dist/gpu-assets.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { type AssetServer } from '@aethervtt/shard-assets';
|
|
1
2
|
import { type World } from '@aethervtt/shard-core';
|
|
2
3
|
import type { GpuContext } from '@aethervtt/shard-gpu';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { Mesh } from '@aethervtt/shard-mesh';
|
|
5
|
+
import { Texture } from '@aethervtt/shard-texture';
|
|
6
|
+
import { MaterialAsset } from './assets.js';
|
|
6
7
|
import type { MaterialType } from './materials.js';
|
|
7
|
-
import type { GpuMemoryData } from './stats.js';
|
|
8
|
+
import type { FrameCounts, GpuMemoryData } from './stats.js';
|
|
8
9
|
export interface GpuMesh {
|
|
9
10
|
version: number;
|
|
10
11
|
generation: number;
|
|
@@ -48,6 +49,8 @@ export interface GpuTexture {
|
|
|
48
49
|
version: number;
|
|
49
50
|
generation: number;
|
|
50
51
|
bytes: number;
|
|
52
|
+
/** Frees the texture and anything made from it (baseline twins); else `texture.destroy()`. */
|
|
53
|
+
destroy?(): void;
|
|
51
54
|
}
|
|
52
55
|
/**
|
|
53
56
|
* GPU copies of meshes, textures, and materials, shared by every pass that draws them (forward,
|
|
@@ -56,6 +59,8 @@ export interface GpuTexture {
|
|
|
56
59
|
*/
|
|
57
60
|
export declare class GpuAssets {
|
|
58
61
|
readonly meshes: Map<Mesh, GpuMesh>;
|
|
62
|
+
/** Where mesh uploads are counted (`RenderStats.current.meshesRebuilt`), when set. */
|
|
63
|
+
counts: FrameCounts | undefined;
|
|
59
64
|
readonly materials: Map<MaterialAsset, GpuMaterial>;
|
|
60
65
|
readonly textures: Map<Texture, GpuTexture>;
|
|
61
66
|
private defaults;
|
|
@@ -75,10 +80,25 @@ export declare class GpuAssets {
|
|
|
75
80
|
readonly gpu: GpuContext;
|
|
76
81
|
readonly memory: GpuMemoryData;
|
|
77
82
|
materialLayout: GPUBindGroupLayout;
|
|
83
|
+
/**
|
|
84
|
+
* The baseline tier's texture strategy (0064), set on a baseline device: one format and one
|
|
85
|
+
* binding dimension per texture, and twins for slots that read it otherwise.
|
|
86
|
+
*/
|
|
87
|
+
baseline: typeof import('./baseline/textures.js') | undefined;
|
|
88
|
+
/** Uploaded this frame on baseline: their bytes go at the next frame (twins take them first). */
|
|
89
|
+
private readonly releaseLater;
|
|
90
|
+
/** Asset guids of textures, for reloading one a twin needs. */
|
|
91
|
+
private readonly guids;
|
|
92
|
+
private world;
|
|
78
93
|
constructor(gpu: GpuContext, memory: GpuMemoryData);
|
|
79
94
|
/** Call once per frame before drawing: rebuilds after device loss and resets readiness. */
|
|
80
95
|
beginFrame(): void;
|
|
81
96
|
mesh(mesh: Mesh): GpuMesh;
|
|
97
|
+
/**
|
|
98
|
+
* Writes a CPU mesh's new data into the buffers of its previous version, when each fits and the
|
|
99
|
+
* mesh is still indexed the same way. Returns false when it needs new buffers.
|
|
100
|
+
*/
|
|
101
|
+
private rewrite;
|
|
82
102
|
private destroyMesh;
|
|
83
103
|
/** Index buffers shared by GPU meshes, per index array. */
|
|
84
104
|
private readonly sharedIndexBuffers;
|
|
@@ -87,8 +107,23 @@ export declare class GpuAssets {
|
|
|
87
107
|
* normals, uvs, uvs1, tangents; f32), and the index buffer every mesh with the same array shares.
|
|
88
108
|
*/
|
|
89
109
|
private gpuMesh;
|
|
110
|
+
/**
|
|
111
|
+
* Frees GPU copies of assets as the asset server drops them: unloaded (an owner released, 0061),
|
|
112
|
+
* or a fallback replaced by the real asset. Returns the unsubscribe.
|
|
113
|
+
*/
|
|
114
|
+
watch(server: AssetServer): () => void;
|
|
115
|
+
/** The GPU objects made for one asset object (a texture, mesh or material): owners.describe (0061). */
|
|
116
|
+
objectsOf(item: unknown): {
|
|
117
|
+
buffers: number;
|
|
118
|
+
textures: number;
|
|
119
|
+
bytes: number;
|
|
120
|
+
};
|
|
90
121
|
/** Frees a mesh's GPU buffers now (they're made again if it's drawn later). */
|
|
91
122
|
releaseMesh(mesh: Mesh): void;
|
|
123
|
+
/** Frees a texture's GPU copy now (it's uploaded again if it's used later). */
|
|
124
|
+
releaseTexture(texture: Texture): void;
|
|
125
|
+
/** Frees a material's uniform buffers now (they're made again if it's drawn later). */
|
|
126
|
+
releaseMaterial(material: MaterialAsset): void;
|
|
92
127
|
/** Uploads a texture (all levels) on first use and when its version changes. */
|
|
93
128
|
texture(texture: Texture): GpuTexture | undefined;
|
|
94
129
|
/** 1x1 white and flat-normal textures for empty slots. */
|
|
@@ -105,6 +140,25 @@ export declare class GpuAssets {
|
|
|
105
140
|
private resolveSlots;
|
|
106
141
|
/** Whether a texture can be uploaded; if its pixels are gone (device loss), reloads it. */
|
|
107
142
|
private available;
|
|
143
|
+
private twins;
|
|
144
|
+
/** What baseline textures ask of this asset layer: reloads, fallbacks, and twin accounting. */
|
|
145
|
+
private twinHost;
|
|
146
|
+
/**
|
|
147
|
+
* Baseline (0064), `render.describe → twins`: the twins waiting for their bytes and how long
|
|
148
|
+
* they've waited, and the last ones made with their waits (0 when made from kept bytes).
|
|
149
|
+
*/
|
|
150
|
+
describeTwins(): {
|
|
151
|
+
pending: {
|
|
152
|
+
texture: string;
|
|
153
|
+
kind: string;
|
|
154
|
+
waitedMs: number;
|
|
155
|
+
}[];
|
|
156
|
+
recent: {
|
|
157
|
+
texture: string;
|
|
158
|
+
kind: string;
|
|
159
|
+
waitedMs: number;
|
|
160
|
+
}[];
|
|
161
|
+
};
|
|
108
162
|
/** The group-1 layout of a material type on this device. */
|
|
109
163
|
layoutOf(type: MaterialType): GPUBindGroupLayout;
|
|
110
164
|
/** The material's uniforms and bind group, uploaded when its version or textures change. */
|
package/dist/gpu-assets.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { assetServer } from "@aethervtt/shard-assets";
|
|
1
|
+
import { AssetServerResource, assetServer } from "@aethervtt/shard-assets";
|
|
2
2
|
import { defineResource } from "@aethervtt/shard-core";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Mesh } from "@aethervtt/shard-mesh";
|
|
4
|
+
import { FORMAT_INFO, Texture, Textures } from "@aethervtt/shard-texture";
|
|
5
|
+
import { MaterialAsset, STANDARD_TYPE, TEXTURE_SLOTS } from "./assets.js";
|
|
5
6
|
import { materialLayout } from "./shaders.js";
|
|
6
7
|
const SRGB_SLOT = [true, false, false, false, true];
|
|
7
8
|
class GpuAssets {
|
|
8
9
|
meshes = /* @__PURE__ */ new Map();
|
|
10
|
+
/** Where mesh uploads are counted (`RenderStats.current.meshesRebuilt`), when set. */
|
|
11
|
+
counts;
|
|
9
12
|
materials = /* @__PURE__ */ new Map();
|
|
10
13
|
textures = /* @__PURE__ */ new Map();
|
|
11
14
|
defaults;
|
|
@@ -31,6 +34,16 @@ class GpuAssets {
|
|
|
31
34
|
gpu;
|
|
32
35
|
memory;
|
|
33
36
|
materialLayout;
|
|
37
|
+
/**
|
|
38
|
+
* The baseline tier's texture strategy (0064), set on a baseline device: one format and one
|
|
39
|
+
* binding dimension per texture, and twins for slots that read it otherwise.
|
|
40
|
+
*/
|
|
41
|
+
baseline;
|
|
42
|
+
/** Uploaded this frame on baseline: their bytes go at the next frame (twins take them first). */
|
|
43
|
+
releaseLater = [];
|
|
44
|
+
/** Asset guids of textures, for reloading one a twin needs. */
|
|
45
|
+
guids = /* @__PURE__ */ new WeakMap();
|
|
46
|
+
world;
|
|
34
47
|
constructor(gpu, memory) {
|
|
35
48
|
this.gpu = gpu;
|
|
36
49
|
this.memory = memory;
|
|
@@ -40,6 +53,11 @@ class GpuAssets {
|
|
|
40
53
|
/** Call once per frame before drawing: rebuilds after device loss and resets readiness. */
|
|
41
54
|
beginFrame() {
|
|
42
55
|
this.ready.clear();
|
|
56
|
+
for (let i = 0; i < this.releaseLater.length; i++) {
|
|
57
|
+
const t = this.releaseLater[i];
|
|
58
|
+
if (!t.keepCpu) t.levels = void 0;
|
|
59
|
+
}
|
|
60
|
+
this.releaseLater.length = 0;
|
|
43
61
|
if (this.generation === this.gpu.generation) return;
|
|
44
62
|
this.generation = this.gpu.generation;
|
|
45
63
|
this.materialLayout = createMaterialLayout(this.gpu);
|
|
@@ -56,7 +74,11 @@ class GpuAssets {
|
|
|
56
74
|
if (mesh.gpu) gm.count = mesh.drawCount;
|
|
57
75
|
return gm;
|
|
58
76
|
}
|
|
59
|
-
if (
|
|
77
|
+
if (this.counts) this.counts.meshesRebuilt++;
|
|
78
|
+
if (gm && gm.generation === gpu.generation) {
|
|
79
|
+
if (!mesh.gpu && this.rewrite(mesh, gm)) return gm;
|
|
80
|
+
this.destroyMesh(gm);
|
|
81
|
+
}
|
|
60
82
|
if (mesh.gpu) return this.gpuMesh(mesh);
|
|
61
83
|
const device = gpu.device;
|
|
62
84
|
const n = mesh.vertexCount;
|
|
@@ -97,6 +119,42 @@ class GpuAssets {
|
|
|
97
119
|
this.meshes.set(mesh, gm);
|
|
98
120
|
return gm;
|
|
99
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Writes a CPU mesh's new data into the buffers of its previous version, when each fits and the
|
|
124
|
+
* mesh is still indexed the same way. Returns false when it needs new buffers.
|
|
125
|
+
*/
|
|
126
|
+
rewrite(mesh, gm) {
|
|
127
|
+
if (gm.sharedVertices || gm.sharedIndices) return false;
|
|
128
|
+
const indices = mesh.indices;
|
|
129
|
+
if (indices === void 0 !== (gm.indices === void 0)) return false;
|
|
130
|
+
const n = mesh.vertexCount;
|
|
131
|
+
const fits = (buffer, bytes) => buffer.size >= bytes;
|
|
132
|
+
if (!fits(gm.positions, n * 12) || !fits(gm.normals, n * 12) || !fits(gm.uvs, n * 8) || !fits(gm.uvs1, n * 8) || !fits(gm.tangents, n * 16) || indices !== void 0 && !fits(gm.indices, indices.byteLength + 3 & ~3))
|
|
133
|
+
return false;
|
|
134
|
+
const queue = this.gpu.device.queue;
|
|
135
|
+
const write = (buffer, data) => {
|
|
136
|
+
if (data.length > 0) queue.writeBuffer(buffer, 0, data, 0, data.length);
|
|
137
|
+
};
|
|
138
|
+
write(gm.positions, mesh.positions);
|
|
139
|
+
write(gm.normals, mesh.normals ?? new Float32Array(n * 3).map((_, i) => i % 3 === 1 ? 1 : 0));
|
|
140
|
+
const uvs = mesh.uvs ?? new Float32Array(n * 2);
|
|
141
|
+
write(gm.uvs, uvs);
|
|
142
|
+
write(gm.uvs1, mesh.uvs1 ?? uvs);
|
|
143
|
+
write(gm.tangents, mesh.tangents ?? new Float32Array(n * 4));
|
|
144
|
+
if (indices) {
|
|
145
|
+
let data = indices;
|
|
146
|
+
if (data instanceof Uint16Array && data.length % 2 === 1) {
|
|
147
|
+
const padded = new Uint16Array(data.length + 1);
|
|
148
|
+
padded.set(data);
|
|
149
|
+
data = padded;
|
|
150
|
+
}
|
|
151
|
+
write(gm.indices, data);
|
|
152
|
+
gm.indexFormat = indices instanceof Uint32Array ? "uint32" : "uint16";
|
|
153
|
+
}
|
|
154
|
+
gm.version = mesh.version;
|
|
155
|
+
gm.count = mesh.drawCount;
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
100
158
|
destroyMesh(gm) {
|
|
101
159
|
if (gm.sharedVertices) return;
|
|
102
160
|
gm.positions.destroy();
|
|
@@ -155,6 +213,46 @@ class GpuAssets {
|
|
|
155
213
|
this.meshes.set(mesh, gm);
|
|
156
214
|
return gm;
|
|
157
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Frees GPU copies of assets as the asset server drops them: unloaded (an owner released, 0061),
|
|
218
|
+
* or a fallback replaced by the real asset. Returns the unsubscribe.
|
|
219
|
+
*/
|
|
220
|
+
watch(server) {
|
|
221
|
+
return server.onUnload((_, item) => {
|
|
222
|
+
if (item instanceof Texture) this.releaseTexture(item);
|
|
223
|
+
else if (item instanceof Mesh) this.releaseMesh(item);
|
|
224
|
+
else if (item instanceof MaterialAsset) this.releaseMaterial(item);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/** The GPU objects made for one asset object (a texture, mesh or material): owners.describe (0061). */
|
|
228
|
+
objectsOf(item) {
|
|
229
|
+
const out = { buffers: 0, textures: 0, bytes: 0 };
|
|
230
|
+
const gpu = this.gpu;
|
|
231
|
+
if (item instanceof Texture) {
|
|
232
|
+
const gt = this.textures.get(item);
|
|
233
|
+
if (gt && gt.generation === gpu.generation) {
|
|
234
|
+
out.textures = 1;
|
|
235
|
+
out.bytes = gt.bytes;
|
|
236
|
+
}
|
|
237
|
+
} else if (item instanceof Mesh) {
|
|
238
|
+
const gm = this.meshes.get(item);
|
|
239
|
+
if (gm && gm.generation === gpu.generation && !gm.sharedVertices) {
|
|
240
|
+
const buffers = [gm.positions, gm.normals, gm.uvs, gm.uvs1, gm.tangents];
|
|
241
|
+
if (gm.indices && !gm.sharedIndices) buffers.push(gm.indices);
|
|
242
|
+
out.buffers = buffers.length;
|
|
243
|
+
for (const b of buffers) out.bytes += b.size;
|
|
244
|
+
}
|
|
245
|
+
} else if (item instanceof MaterialAsset) {
|
|
246
|
+
const gm = this.materials.get(item);
|
|
247
|
+
if (gm && gm.generation === gpu.generation) {
|
|
248
|
+
const buffers = [gm.buffer, gm.textureBuffer];
|
|
249
|
+
if (gm.ownBuffer) buffers.push(gm.ownBuffer);
|
|
250
|
+
out.buffers = buffers.length;
|
|
251
|
+
for (const b of buffers) out.bytes += b.size;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return out;
|
|
255
|
+
}
|
|
158
256
|
/** Frees a mesh's GPU buffers now (they're made again if it's drawn later). */
|
|
159
257
|
releaseMesh(mesh) {
|
|
160
258
|
const gm = this.meshes.get(mesh);
|
|
@@ -162,6 +260,30 @@ class GpuAssets {
|
|
|
162
260
|
if (gm.generation === this.gpu.generation) this.destroyMesh(gm);
|
|
163
261
|
this.meshes.delete(mesh);
|
|
164
262
|
}
|
|
263
|
+
/** Frees a texture's GPU copy now (it's uploaded again if it's used later). */
|
|
264
|
+
releaseTexture(texture) {
|
|
265
|
+
const gt = this.textures.get(texture);
|
|
266
|
+
if (!gt) return;
|
|
267
|
+
if (gt.generation === this.gpu.generation) {
|
|
268
|
+
if (gt.destroy) gt.destroy();
|
|
269
|
+
else gt.texture.destroy();
|
|
270
|
+
this.memory.textures--;
|
|
271
|
+
this.memory.textureBytes -= gt.bytes;
|
|
272
|
+
}
|
|
273
|
+
this.textures.delete(texture);
|
|
274
|
+
}
|
|
275
|
+
/** Frees a material's uniform buffers now (they're made again if it's drawn later). */
|
|
276
|
+
releaseMaterial(material) {
|
|
277
|
+
const gm = this.materials.get(material);
|
|
278
|
+
if (!gm) return;
|
|
279
|
+
if (gm.generation === this.gpu.generation) {
|
|
280
|
+
gm.buffer.destroy();
|
|
281
|
+
gm.textureBuffer.destroy();
|
|
282
|
+
gm.ownBuffer?.destroy();
|
|
283
|
+
}
|
|
284
|
+
this.materials.delete(material);
|
|
285
|
+
this.ready.delete(material);
|
|
286
|
+
}
|
|
165
287
|
/** Uploads a texture (all levels) on first use and when its version changes. */
|
|
166
288
|
texture(texture) {
|
|
167
289
|
const gpu = this.gpu;
|
|
@@ -174,7 +296,16 @@ class GpuAssets {
|
|
|
174
296
|
this.memory.textures--;
|
|
175
297
|
this.memory.textureBytes -= existing.bytes;
|
|
176
298
|
}
|
|
177
|
-
existing?.
|
|
299
|
+
if (existing?.destroy) existing.destroy();
|
|
300
|
+
else existing?.texture.destroy();
|
|
301
|
+
if (this.baseline) {
|
|
302
|
+
const out2 = new this.baseline.BaselineTexture(this.twinHost(), texture);
|
|
303
|
+
this.textures.set(texture, out2);
|
|
304
|
+
this.memory.textures++;
|
|
305
|
+
this.memory.textureBytes += out2.bytes;
|
|
306
|
+
if (!texture.keepCpu) this.releaseLater.push(texture);
|
|
307
|
+
return out2;
|
|
308
|
+
}
|
|
178
309
|
const info = FORMAT_INFO[texture.format];
|
|
179
310
|
const srgbView = texture.gpuOnly ? void 0 : info.srgbView;
|
|
180
311
|
const layers = texture.faces * texture.layers;
|
|
@@ -229,6 +360,14 @@ class GpuAssets {
|
|
|
229
360
|
defaultTextures() {
|
|
230
361
|
const gpu = this.gpu;
|
|
231
362
|
if (this.defaults && this.defaults.white.generation === gpu.generation) return this.defaults;
|
|
363
|
+
if (this.baseline) {
|
|
364
|
+
this.defaults = {
|
|
365
|
+
white: this.baseline.solidTexture(this.twinHost(), [255, 255, 255, 255]),
|
|
366
|
+
normal: this.baseline.solidTexture(this.twinHost(), [128, 128, 255, 255])
|
|
367
|
+
};
|
|
368
|
+
this.samplers.clear();
|
|
369
|
+
return this.defaults;
|
|
370
|
+
}
|
|
232
371
|
const solid = (label, rgba) => {
|
|
233
372
|
const texture = gpu.device.createTexture({
|
|
234
373
|
label,
|
|
@@ -289,6 +428,7 @@ class GpuAssets {
|
|
|
289
428
|
return ready;
|
|
290
429
|
}
|
|
291
430
|
resolveSlots(world, material) {
|
|
431
|
+
this.world = world;
|
|
292
432
|
const store = world.tryResource(Textures);
|
|
293
433
|
material.sync();
|
|
294
434
|
const type = material.type;
|
|
@@ -300,7 +440,7 @@ class GpuAssets {
|
|
|
300
440
|
this.ownTextures[i] = void 0;
|
|
301
441
|
continue;
|
|
302
442
|
}
|
|
303
|
-
const texture = store?.get(ref);
|
|
443
|
+
const texture = store?.get(ref) ?? missingTexture(world, ref);
|
|
304
444
|
if (!texture || !this.available(world, texture, ref)) return false;
|
|
305
445
|
this.ownTextures[i] = texture;
|
|
306
446
|
}
|
|
@@ -312,14 +452,22 @@ class GpuAssets {
|
|
|
312
452
|
this.slotTextures[i] = void 0;
|
|
313
453
|
continue;
|
|
314
454
|
}
|
|
315
|
-
const texture = store?.get(ref);
|
|
455
|
+
const texture = store?.get(ref) ?? missingTexture(world, ref);
|
|
316
456
|
if (!texture || !this.available(world, texture, ref)) return false;
|
|
317
457
|
this.slotTextures[i] = texture;
|
|
318
458
|
}
|
|
459
|
+
if (this.baseline && material.value.deferUntilReady) {
|
|
460
|
+
for (let i = 0; i < 5; i++) {
|
|
461
|
+
const texture = this.slotTextures[i];
|
|
462
|
+
const g = texture ? this.texture(texture) : void 0;
|
|
463
|
+
if (g && g.waiting(SRGB_SLOT[i], false)) return false;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
319
466
|
return true;
|
|
320
467
|
}
|
|
321
468
|
/** Whether a texture can be uploaded; if its pixels are gone (device loss), reloads it. */
|
|
322
469
|
available(world, texture, ref) {
|
|
470
|
+
if (ref.guid) this.guids.set(texture, ref.guid);
|
|
323
471
|
if (texture.levels || this.textures.has(texture)) return true;
|
|
324
472
|
if (ref.guid && !this.reloading.has(ref.guid)) {
|
|
325
473
|
this.reloading.add(ref.guid);
|
|
@@ -328,6 +476,34 @@ class GpuAssets {
|
|
|
328
476
|
}
|
|
329
477
|
return false;
|
|
330
478
|
}
|
|
479
|
+
twins;
|
|
480
|
+
/** What baseline textures ask of this asset layer: reloads, fallbacks, and twin accounting. */
|
|
481
|
+
twinHost() {
|
|
482
|
+
const assets = this;
|
|
483
|
+
this.twins ??= new this.baseline.Twins({
|
|
484
|
+
gpu: this.gpu,
|
|
485
|
+
get world() {
|
|
486
|
+
return assets.world;
|
|
487
|
+
},
|
|
488
|
+
guidOf: (texture) => this.guids.get(texture),
|
|
489
|
+
reload: (texture) => {
|
|
490
|
+
const guid = this.guids.get(texture);
|
|
491
|
+
const world = this.world;
|
|
492
|
+
if (!guid || !world || this.reloading.has(guid)) return;
|
|
493
|
+
this.reloading.add(guid);
|
|
494
|
+
void assetServer(world).reload(guid).finally(() => this.reloading.delete(guid));
|
|
495
|
+
},
|
|
496
|
+
white: () => this.defaultTextures().white
|
|
497
|
+
});
|
|
498
|
+
return this.twins;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Baseline (0064), `render.describe → twins`: the twins waiting for their bytes and how long
|
|
502
|
+
* they've waited, and the last ones made with their waits (0 when made from kept bytes).
|
|
503
|
+
*/
|
|
504
|
+
describeTwins() {
|
|
505
|
+
return this.twins?.describe() ?? { pending: [], recent: [] };
|
|
506
|
+
}
|
|
331
507
|
/** The group-1 layout of a material type on this device. */
|
|
332
508
|
layoutOf(type) {
|
|
333
509
|
return type.bindGroupLayout(this.gpu, standardEntries());
|
|
@@ -451,7 +627,7 @@ class GpuAssets {
|
|
|
451
627
|
});
|
|
452
628
|
} else {
|
|
453
629
|
const g = gm.ownBound[i] ?? defaults.white;
|
|
454
|
-
entries.push({ binding: binding++, resource: g.linear });
|
|
630
|
+
entries.push({ binding: binding++, resource: type.colors.has(name) ? g.srgb : g.linear });
|
|
455
631
|
}
|
|
456
632
|
entries.push({ binding: binding++, resource: this.sampler("repeat", "linear") });
|
|
457
633
|
}
|
|
@@ -484,6 +660,13 @@ function standardEntries() {
|
|
|
484
660
|
function createMaterialLayout(gpu) {
|
|
485
661
|
return STANDARD_TYPE.bindGroupLayout(gpu, standardEntries());
|
|
486
662
|
}
|
|
663
|
+
function missingTexture(world, ref) {
|
|
664
|
+
if (ref.guid === void 0) return void 0;
|
|
665
|
+
const server = world.tryResource(AssetServerResource);
|
|
666
|
+
if (!server || server.entry(ref.guid)) return void 0;
|
|
667
|
+
server.missing(ref, "Texture");
|
|
668
|
+
return world.tryResource(Textures)?.byGuid(ref.guid);
|
|
669
|
+
}
|
|
487
670
|
const GpuAssetsResource = defineResource("render/GpuAssets", {
|
|
488
671
|
description: "GPU copies of meshes, textures, and materials."
|
|
489
672
|
});
|
package/dist/graph.d.ts
CHANGED
|
@@ -103,8 +103,14 @@ export declare const RenderPhase: {
|
|
|
103
103
|
readonly Opaque: 300;
|
|
104
104
|
readonly Lighting: 350;
|
|
105
105
|
readonly Sky: 400;
|
|
106
|
+
/** Coplanar ground bands (0057): after the sky, which draws where no depth was written. */
|
|
107
|
+
readonly Ground: 420;
|
|
106
108
|
readonly Sprites: 450;
|
|
107
109
|
readonly Transparent: 500;
|
|
110
|
+
/** Projected fog's composite (0058): after transparent objects, before the overlay band. */
|
|
111
|
+
readonly Fog: 520;
|
|
112
|
+
/** Ground bands from the overlay band up, over fog (0058). */
|
|
113
|
+
readonly Overlay3d: 530;
|
|
108
114
|
readonly Effects3d: 550;
|
|
109
115
|
readonly Resolve: 600;
|
|
110
116
|
readonly Post: 700;
|
|
@@ -164,15 +170,20 @@ export declare class RenderGraph {
|
|
|
164
170
|
private lastViews;
|
|
165
171
|
private readonly viewData;
|
|
166
172
|
constructor(gpu: GpuContext);
|
|
173
|
+
/** Rejects pending captures and destroys the timer's query set (the app is being disposed). */
|
|
174
|
+
dispose(): void;
|
|
167
175
|
addNode(name: string, node: NodeDescriptor): void;
|
|
168
176
|
removeNode(name: string): void;
|
|
169
177
|
hasNode(name: string): boolean;
|
|
178
|
+
/** Every node's name, in the order they were added. */
|
|
179
|
+
nodeNames(): string[];
|
|
170
180
|
/** The data of a view rendered in the last frame. */
|
|
171
181
|
lastViewData(view: string): Record<string, unknown> | undefined;
|
|
172
182
|
/** Declares a transient texture any node can write, read, or attach by name. */
|
|
173
183
|
declare(texture: TransientTexture): void;
|
|
174
184
|
/** The resolution with every node enabled and no aliases. */
|
|
175
185
|
resolve(): ResolvedGraph;
|
|
186
|
+
/** `off`: nodes that don't run on this device (features the baseline tier doesn't support). */
|
|
176
187
|
private resolveFor;
|
|
177
188
|
/** Whether a node after `k` loads or reads `resource` (so its contents must be stored). */
|
|
178
189
|
private loadedLater;
|