rayzee 8.8.2 → 9.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/README.md +210 -20
- package/dist/{NeuralRetouch-CYOJ21Wn.js → NeuralRetouch-BRquqfmZ.js} +1 -1
- package/dist/{NeuralSuperRes-Dc6jRYke.js → NeuralSuperRes-CbNvQjCw.js} +5 -2
- package/dist/{ToneMapGPU-BVE852iN.js → ToneMapGPU-DpUZDMbQ.js} +2 -1
- package/dist/rayzee.es.js +10795 -5324
- package/dist/rayzee.umd.js +279 -268
- package/package.json +1 -1
- package/src/AssetConfig.js +4 -0
- package/src/EngineDefaults.js +60 -76
- package/src/EngineEvents.js +7 -0
- package/src/EngineIssues.js +6 -0
- package/src/Headless.js +3 -1
- package/src/Passes/AIUpscaler.js +2 -1
- package/src/Passes/OIDNDenoiser.js +3 -6
- package/src/Passes/OIDNTemporalHistory.js +25 -7
- package/src/PathTracerApp.js +313 -25
- package/src/Processor/ArchiveCache.js +120 -0
- package/src/Processor/ArchiveReader.js +179 -32
- package/src/Processor/AssetLoader.js +523 -76
- package/src/Processor/AtmosphereModel.js +465 -0
- package/src/Processor/BVHBuilder.js +4 -1
- package/src/Processor/BVHLeafFold.js +100 -0
- package/src/Processor/BVHRefitter.js +98 -214
- package/src/Processor/ChunkedRecords.js +189 -4
- package/src/Processor/EmissiveTriangleBuilder.js +8 -0
- package/src/Processor/GeometryExtractor.js +238 -40
- package/src/Processor/InstanceTable.js +55 -37
- package/src/Processor/PBRT/PBRTCurves.js +104 -27
- package/src/Processor/PBRT/PBRTParser.js +132 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +210 -55
- package/src/Processor/PBRT/buffers.js +27 -0
- package/src/Processor/PBRT/index.js +55 -15
- package/src/Processor/PackedRayBuffer.js +10 -13
- package/src/Processor/ParallelBVHBuilder.js +36 -15
- package/src/Processor/PhysicalSky.js +308 -0
- package/src/Processor/QueueManager.js +1 -0
- package/src/Processor/SceneProcessor.js +1132 -181
- package/src/Processor/SplitBLAS.js +249 -0
- package/src/Processor/SunPosition.js +41 -0
- package/src/Processor/TextureCreator.js +21 -0
- package/src/Processor/Workers/AIUpscalerWorker.js +79 -52
- package/src/Processor/Workers/BVHWorker.js +17 -13
- package/src/Processor/ZipReader.js +207 -0
- package/src/RenderSettings.js +49 -8
- package/src/SceneState/SceneState.js +367 -0
- package/src/SceneState/portable.js +52 -0
- package/src/Stages/MotionVector.js +8 -42
- package/src/Stages/NRD.js +30 -21
- package/src/Stages/NormalDepth.js +12 -23
- package/src/Stages/PathTracer.js +114 -6
- package/src/Stages/PathTracerStage.js +251 -104
- package/src/Storage/BLASCache.js +190 -0
- package/src/Storage/CDFCache.js +77 -0
- package/src/Storage/DownloadCache.js +266 -0
- package/src/Storage/GeometrySpill.js +217 -0
- package/src/Storage/SceneGraphCodec.js +503 -0
- package/src/Storage/SpillStore.js +112 -0
- package/src/Storage/StorageManager.js +934 -0
- package/src/Storage/StorageOps.js +137 -0
- package/src/Storage/StorageWorker.js +37 -0
- package/src/Storage/events.js +29 -0
- package/src/Storage/identity.js +77 -0
- package/src/Storage/index.js +9 -0
- package/src/Storage/inlineTransport.js +24 -0
- package/src/Storage/locks.js +99 -0
- package/src/Storage/openStorage.js +87 -0
- package/src/Storage/sceneCachePolicy.js +12 -0
- package/src/Storage/shared.js +9 -0
- package/src/Storage/transport.js +62 -0
- package/src/TSL/Atmosphere.js +684 -0
- package/src/TSL/BVHTraversal.js +288 -239
- package/src/TSL/CameraRay.js +99 -14
- package/src/TSL/Common.js +11 -2
- package/src/TSL/DebugKernel.js +2 -2
- package/src/TSL/Displacement.js +6 -6
- package/src/TSL/EmissiveSampling.js +5 -5
- package/src/TSL/EnvironmentCDF.js +176 -0
- package/src/TSL/GenerateKernel.js +5 -5
- package/src/TSL/HitFacet.js +4 -4
- package/src/TSL/LightsDirect.js +8 -8
- package/src/TSL/LightsSampling.js +55 -37
- package/src/TSL/Random.js +1 -0
- package/src/TSL/ShadeKernel.js +75 -19
- package/src/TSL/Sun.js +40 -0
- package/src/TSL/TextureSampling.js +6 -7
- package/src/index.js +22 -0
- package/src/managers/CameraManager.js +412 -56
- package/src/managers/DenoisingManager.js +4 -3
- package/src/managers/EnvironmentManager.js +263 -33
- package/src/managers/GoboManager.js +19 -8
- package/src/managers/IESManager.js +3 -3
- package/src/managers/InteractionManager.js +30 -2
- package/src/managers/LightManager.js +98 -5
- package/src/managers/MaterialDataManager.js +22 -2
- package/src/managers/TransformManager.js +26 -0
- package/src/managers/UniformManager.js +11 -7
- package/src/managers/VideoRenderManager.js +38 -17
- package/src/managers/ViewCamera.js +77 -0
- package/src/managers/WalkControls.js +262 -0
- package/src/managers/helpers/OutlineHelper.js +22 -2
- package/src/managers/timeline/CameraTrack.js +235 -0
- package/src/managers/timeline/TimelineManager.js +146 -0
- package/src/version.js +3 -0
- package/src/Processor/ProceduralSky.js +0 -291
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { TRIANGLE_DATA_LAYOUT } from '../EngineDefaults.js';
|
|
2
|
+
import { rebaseNodes } from './BVHLeafFold.js';
|
|
3
|
+
|
|
4
|
+
const FLOATS_PER_NODE = 16;
|
|
5
|
+
const A = TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET;
|
|
6
|
+
const B = TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET;
|
|
7
|
+
const C = TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sorts records [start, start + count) in place into spatial pieces of at most `pieceTriangles`,
|
|
11
|
+
* halving at the centroid median along the longest axis — so a mesh too large to build at once
|
|
12
|
+
* becomes pieces that build one per worker and join under a few nodes ({@link joinPieces}).
|
|
13
|
+
* @param {import('./ChunkedRecords.js').ChunkedRecords} records
|
|
14
|
+
* @param {{pause?: function(): Promise}} [options] - awaited every million records moved or so
|
|
15
|
+
* @returns {Promise<{order: Uint32Array, pieces: Array<{start: number, count: number}>, tree: Object}>}
|
|
16
|
+
* `order[ j ]` is the index, within the range, that record j held before; piece starts are
|
|
17
|
+
* relative to `start`, and `tree` is the halving as `{ left, right }` down to `{ piece }`.
|
|
18
|
+
*/
|
|
19
|
+
export async function partitionRange( records, start, count, pieceTriangles, { pause = null } = {} ) {
|
|
20
|
+
|
|
21
|
+
const lanes = records.lanesPerRecord;
|
|
22
|
+
// One array per axis, swapped in step with `order`, so every pass reads memory in sequence.
|
|
23
|
+
const axes = [ new Float32Array( count ), new Float32Array( count ), new Float32Array( count ) ];
|
|
24
|
+
const [ cx, cy, cz ] = axes;
|
|
25
|
+
for ( let j = 0; j < count; ) {
|
|
26
|
+
|
|
27
|
+
const chunk = records.chunkFor( start + j );
|
|
28
|
+
const f = new Float32Array( chunk.buffer, chunk.byteOffset, chunk.length );
|
|
29
|
+
const end = Math.min( count, ( ( ( ( start + j ) / records.recordsPerChunk ) | 0 ) + 1 ) * records.recordsPerChunk - start );
|
|
30
|
+
for ( let d = records.baseOf( start + j ); j < end; j ++, d += lanes ) {
|
|
31
|
+
|
|
32
|
+
cx[ j ] = f[ d + A ] + f[ d + B ] + f[ d + C ];
|
|
33
|
+
cy[ j ] = f[ d + A + 1 ] + f[ d + B + 1 ] + f[ d + C + 1 ];
|
|
34
|
+
cz[ j ] = f[ d + A + 2 ] + f[ d + B + 2 ] + f[ d + C + 2 ];
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const order = new Uint32Array( count );
|
|
41
|
+
for ( let j = 0; j < count; j ++ ) order[ j ] = j;
|
|
42
|
+
|
|
43
|
+
const pieces = [];
|
|
44
|
+
const split = ( lo, hi ) => {
|
|
45
|
+
|
|
46
|
+
if ( hi - lo <= pieceTriangles ) {
|
|
47
|
+
|
|
48
|
+
pieces.push( { start: lo, count: hi - lo } );
|
|
49
|
+
return { piece: pieces.length - 1 };
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const mid = lo + ( ( hi - lo ) >> 1 );
|
|
54
|
+
selectNth( order, axes, longestAxis( axes, lo, hi ), lo, hi, mid );
|
|
55
|
+
return { left: split( lo, mid ), right: split( mid, hi ) };
|
|
56
|
+
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const tree = split( 0, count );
|
|
60
|
+
await pause?.();
|
|
61
|
+
await permuteInPlace( records, start, count, order, pause );
|
|
62
|
+
return { order, pieces, tree };
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function longestAxis( axes, lo, hi ) {
|
|
67
|
+
|
|
68
|
+
let best = 0, widest = - 1;
|
|
69
|
+
for ( let a = 0; a < 3; a ++ ) {
|
|
70
|
+
|
|
71
|
+
const c = axes[ a ];
|
|
72
|
+
let min = Infinity, max = - Infinity;
|
|
73
|
+
for ( let i = lo; i < hi; i ++ ) {
|
|
74
|
+
|
|
75
|
+
const v = c[ i ];
|
|
76
|
+
if ( v < min ) min = v;
|
|
77
|
+
if ( v > max ) max = v;
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if ( max - min > widest ) {
|
|
82
|
+
|
|
83
|
+
widest = max - min;
|
|
84
|
+
best = a;
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return best;
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Quickselect with a three-way partition, so runs of equal keys cannot make it quadratic, and
|
|
95
|
+
// pivots sampled with a fixed seed: the first, middle and last of a terrain grid's repeating rows
|
|
96
|
+
// took a thousand passes. Fixed, so the same mesh always builds the same BVH.
|
|
97
|
+
function selectNth( order, axes, axis, lo, hi, k ) {
|
|
98
|
+
|
|
99
|
+
const key = axes[ axis ];
|
|
100
|
+
const [ c0, c1, c2 ] = axes;
|
|
101
|
+
const swap = ( i, j ) => {
|
|
102
|
+
|
|
103
|
+
let t = order[ i ]; order[ i ] = order[ j ]; order[ j ] = t;
|
|
104
|
+
t = c0[ i ]; c0[ i ] = c0[ j ]; c0[ j ] = t;
|
|
105
|
+
t = c1[ i ]; c1[ i ] = c1[ j ]; c1[ j ] = t;
|
|
106
|
+
t = c2[ i ]; c2[ i ] = c2[ j ]; c2[ j ] = t;
|
|
107
|
+
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
let seed = 0x9e3779b9;
|
|
111
|
+
const sample = () => {
|
|
112
|
+
|
|
113
|
+
seed ^= seed << 13;
|
|
114
|
+
seed ^= seed >>> 17;
|
|
115
|
+
seed ^= seed << 5;
|
|
116
|
+
return key[ lo + ( seed >>> 0 ) % ( hi - lo ) ];
|
|
117
|
+
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
while ( hi - lo > 1 ) {
|
|
121
|
+
|
|
122
|
+
const a = sample(), b = sample(), c = sample();
|
|
123
|
+
const pivot = a < b ? ( b < c ? b : a < c ? c : a ) : ( a < c ? a : b < c ? c : b );
|
|
124
|
+
|
|
125
|
+
let lt = lo, i = lo, gt = hi;
|
|
126
|
+
while ( i < gt ) {
|
|
127
|
+
|
|
128
|
+
const v = key[ i ];
|
|
129
|
+
if ( v < pivot ) swap( lt ++, i ++ );
|
|
130
|
+
else if ( v > pivot ) swap( i, -- gt );
|
|
131
|
+
else i ++;
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if ( k < lt ) hi = lt;
|
|
136
|
+
else if ( k >= gt ) lo = gt;
|
|
137
|
+
else return;
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Record j takes record order[ j ], following each cycle with one record held aside.
|
|
144
|
+
async function permuteInPlace( records, start, count, order, pause ) {
|
|
145
|
+
|
|
146
|
+
const lanes = records.lanesPerRecord;
|
|
147
|
+
const held = new Uint32Array( lanes );
|
|
148
|
+
const done = new Uint8Array( count );
|
|
149
|
+
let moved = 0;
|
|
150
|
+
|
|
151
|
+
for ( let j = 0; j < count; j ++ ) {
|
|
152
|
+
|
|
153
|
+
if ( done[ j ] ) continue;
|
|
154
|
+
done[ j ] = 1;
|
|
155
|
+
if ( order[ j ] === j ) continue;
|
|
156
|
+
|
|
157
|
+
let chunk = records.chunkFor( start + j ), base = records.baseOf( start + j );
|
|
158
|
+
for ( let l = 0; l < lanes; l ++ ) held[ l ] = chunk[ base + l ];
|
|
159
|
+
|
|
160
|
+
for ( let k = j; ; ) {
|
|
161
|
+
|
|
162
|
+
const s = order[ k ];
|
|
163
|
+
const to = chunk, at = base;
|
|
164
|
+
if ( s === j ) {
|
|
165
|
+
|
|
166
|
+
for ( let l = 0; l < lanes; l ++ ) to[ at + l ] = held[ l ];
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
chunk = records.chunkFor( start + s );
|
|
172
|
+
base = records.baseOf( start + s );
|
|
173
|
+
for ( let l = 0; l < lanes; l ++ ) to[ at + l ] = chunk[ base + l ];
|
|
174
|
+
done[ s ] = 1;
|
|
175
|
+
k = s;
|
|
176
|
+
if ( pause && ( ++ moved & 0xfffff ) === 0 ) await pause();
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* One BLAS from the pieces of {@link partitionRange}: the halving becomes inner nodes ahead of
|
|
186
|
+
* the pieces' own nodes, which are rebased in place to follow them, so children always sit after
|
|
187
|
+
* their parent as a refit requires. Handed back as parts, never one array the size of the mesh:
|
|
188
|
+
* a 600 MB request is what failed an 80M-triangle build with memory to spare.
|
|
189
|
+
* @param {Object} tree
|
|
190
|
+
* @param {Array<{start: number, count: number}>} pieces
|
|
191
|
+
* @param {Array<{bvhData: Float32Array, originalToBvh: ?Uint32Array, aabb: ArrayLike<number>}>} built - per piece
|
|
192
|
+
* @param {Uint32Array} order - from partitionRange
|
|
193
|
+
* @returns {{bvhData: Float32Array[], nodeCount: number, originalToBvh: Uint32Array}} the join's nodes
|
|
194
|
+
* first, then each piece's; `originalToBvh` as one build of the whole range returns it
|
|
195
|
+
*/
|
|
196
|
+
export function joinPieces( tree, pieces, built, order ) {
|
|
197
|
+
|
|
198
|
+
const inner = pieces.length - 1;
|
|
199
|
+
const bases = [];
|
|
200
|
+
let total = inner;
|
|
201
|
+
for ( const { bvhData } of built ) {
|
|
202
|
+
|
|
203
|
+
bases.push( total );
|
|
204
|
+
total += bvhData.length / FLOATS_PER_NODE;
|
|
205
|
+
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const data = new Float32Array( inner * FLOATS_PER_NODE );
|
|
209
|
+
const idx = new Uint32Array( data.buffer );
|
|
210
|
+
const parts = [ data ];
|
|
211
|
+
const originalToBvh = new Uint32Array( order.length );
|
|
212
|
+
|
|
213
|
+
for ( let k = 0; k < pieces.length; k ++ ) {
|
|
214
|
+
|
|
215
|
+
const { bvhData, originalToBvh: pieceOrder } = built[ k ];
|
|
216
|
+
built[ k ].bvhData = built[ k ].originalToBvh = null;
|
|
217
|
+
const base = bases[ k ];
|
|
218
|
+
const { start, count } = pieces[ k ];
|
|
219
|
+
rebaseNodes( new Uint32Array( bvhData.buffer, bvhData.byteOffset, bvhData.length ), base, start );
|
|
220
|
+
|
|
221
|
+
parts.push( bvhData );
|
|
222
|
+
for ( let i = 0; i < count; i ++ ) originalToBvh[ order[ start + i ] ] = start + ( pieceOrder ? pieceOrder[ i ] : i );
|
|
223
|
+
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let next = 0;
|
|
227
|
+
const place = node => {
|
|
228
|
+
|
|
229
|
+
if ( node.piece !== undefined ) return { index: bases[ node.piece ], box: built[ node.piece ].aabb };
|
|
230
|
+
|
|
231
|
+
const index = next ++;
|
|
232
|
+
const l = place( node.left );
|
|
233
|
+
const r = place( node.right );
|
|
234
|
+
const o = index * FLOATS_PER_NODE;
|
|
235
|
+
data[ o ] = l.box[ 0 ]; data[ o + 1 ] = l.box[ 1 ]; data[ o + 2 ] = l.box[ 2 ]; idx[ o + 3 ] = l.index;
|
|
236
|
+
data[ o + 4 ] = l.box[ 3 ]; data[ o + 5 ] = l.box[ 4 ]; data[ o + 6 ] = l.box[ 5 ]; idx[ o + 7 ] = r.index;
|
|
237
|
+
data[ o + 8 ] = r.box[ 0 ]; data[ o + 9 ] = r.box[ 1 ]; data[ o + 10 ] = r.box[ 2 ];
|
|
238
|
+
data[ o + 12 ] = r.box[ 3 ]; data[ o + 13 ] = r.box[ 4 ]; data[ o + 14 ] = r.box[ 5 ];
|
|
239
|
+
|
|
240
|
+
const box = [ 0, 1, 2 ].map( a => Math.min( l.box[ a ], r.box[ a ] ) )
|
|
241
|
+
.concat( [ 3, 4, 5 ].map( a => Math.max( l.box[ a ], r.box[ a ] ) ) );
|
|
242
|
+
return { index, box };
|
|
243
|
+
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
place( tree );
|
|
247
|
+
return { bvhData: parts, nodeCount: total, originalToBvh };
|
|
248
|
+
|
|
249
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the sun stands, from local solar time, day of the year and latitude. Declination is
|
|
3
|
+
* Spencer's (1971) series; azimuth is clockwise from north. Degrees in and out.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const RAD = Math.PI / 180;
|
|
7
|
+
|
|
8
|
+
function declination( dayOfYear ) {
|
|
9
|
+
|
|
10
|
+
const g = 2 * Math.PI * ( dayOfYear - 1 ) / 365;
|
|
11
|
+
return 0.006918 - 0.399912 * Math.cos( g ) + 0.070257 * Math.sin( g ) - 0.006758 * Math.cos( 2 * g )
|
|
12
|
+
+ 0.000907 * Math.sin( 2 * g ) - 0.002697 * Math.cos( 3 * g ) + 0.00148 * Math.sin( 3 * g );
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @returns {{ azimuth: number, elevation: number }} */
|
|
17
|
+
export function sunPosition( { hours, dayOfYear, latitude } ) {
|
|
18
|
+
|
|
19
|
+
const d = declination( dayOfYear );
|
|
20
|
+
const h = ( hours - 12 ) * 15 * RAD;
|
|
21
|
+
const lat = latitude * RAD;
|
|
22
|
+
const sinEl = Math.sin( lat ) * Math.sin( d ) + Math.cos( lat ) * Math.cos( d ) * Math.cos( h );
|
|
23
|
+
const elevation = Math.asin( Math.min( 1, Math.max( - 1, sinEl ) ) ) / RAD;
|
|
24
|
+
const azimuth = ( Math.atan2( Math.sin( h ), Math.cos( h ) * Math.sin( lat ) - Math.tan( d ) * Math.cos( lat ) ) / RAD + 540 ) % 360;
|
|
25
|
+
return { azimuth, elevation };
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The solar time the sun reaches `elevation`, before or after noon; noon or midnight when it never does. */
|
|
30
|
+
export function timeForSunElevation( { elevation, dayOfYear, latitude, afternoon = false } ) {
|
|
31
|
+
|
|
32
|
+
const d = declination( dayOfYear );
|
|
33
|
+
const lat = latitude * RAD;
|
|
34
|
+
const cosH = ( Math.sin( elevation * RAD ) - Math.sin( lat ) * Math.sin( d ) ) / ( Math.cos( lat ) * Math.cos( d ) );
|
|
35
|
+
const h = Math.acos( Math.min( 1, Math.max( - 1, cosH ) ) ) / RAD / 15;
|
|
36
|
+
return afternoon ? 12 + h : 12 - h;
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Day of the year of the 21st of `month` (1–12), near each solstice and equinox. */
|
|
41
|
+
export const dayOfYearForMonth = month => [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ][ month - 1 ] + 21;
|
|
@@ -251,6 +251,13 @@ class SmartBufferPool {
|
|
|
251
251
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
/** Stops counting a buffer that is being let go rather than returned. */
|
|
255
|
+
forget( buffer ) {
|
|
256
|
+
|
|
257
|
+
this.memoryUsage = Math.max( 0, this.memoryUsage - buffer.buffer.byteLength );
|
|
258
|
+
|
|
259
|
+
}
|
|
260
|
+
|
|
254
261
|
releaseBuffer( buffer, Type = Float32Array ) {
|
|
255
262
|
|
|
256
263
|
// Recover the full allocated size from the underlying ArrayBuffer
|
|
@@ -1407,6 +1414,20 @@ export class TextureCreator {
|
|
|
1407
1414
|
|
|
1408
1415
|
// Enhanced disposal
|
|
1409
1416
|
texture.userData = { buffer: data, bufferType: Uint8Array };
|
|
1417
|
+
|
|
1418
|
+
// Nothing reads these pixels once they are on the GPU — a rebuild packs new arrays from the
|
|
1419
|
+
// three.js sources — so the CPU copy goes as soon as three.js has uploaded it. Not back to
|
|
1420
|
+
// the pool, which would keep it alive; a cache lookup then sees `buffer: null` and rebuilds.
|
|
1421
|
+
texture.onUpdate = () => {
|
|
1422
|
+
|
|
1423
|
+
const buffer = texture.userData.buffer;
|
|
1424
|
+
if ( ! buffer ) return;
|
|
1425
|
+
this.bufferPool.forget( buffer );
|
|
1426
|
+
texture.userData.buffer = null;
|
|
1427
|
+
texture.image.data = null;
|
|
1428
|
+
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1410
1431
|
const originalDispose = texture.dispose.bind( texture );
|
|
1411
1432
|
texture.dispose = () => {
|
|
1412
1433
|
|
|
@@ -14,12 +14,16 @@
|
|
|
14
14
|
* { type: 'error', message, id? }
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { openInlineStorage } from '../../Storage/StorageManager.js';
|
|
18
|
+
import { DownloadCache } from '../../Storage/DownloadCache.js';
|
|
19
|
+
|
|
17
20
|
// Asset config supplied via 'load' message from the main thread.
|
|
18
21
|
// Defaults match the upstream Rayzee deployment; override via configureAssets().
|
|
19
22
|
let _assetConfig = {
|
|
20
23
|
ortRuntimeUrl: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.webgpu.bundle.min.mjs',
|
|
21
24
|
ortWasmPaths: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/',
|
|
22
25
|
cacheNamespace: 'rayzee',
|
|
26
|
+
storage: 'auto',
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
let ort = null;
|
|
@@ -38,98 +42,120 @@ async function getOrt() {
|
|
|
38
42
|
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
let session = null;
|
|
46
|
+
let currentModelUrl = null;
|
|
47
|
+
let downloads = null;
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
// ─── Model cache (the engine's download storage, written from this worker) ─────
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
function getDownloads() {
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
downloads ??= ( async () => {
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
let currentModelUrl = null;
|
|
55
|
+
if ( _assetConfig.storage === false || ! navigator.storage?.getDirectory ) return new DownloadCache( null );
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
try {
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
const top = await navigator.storage.getDirectory();
|
|
60
|
+
const root = await top.getDirectoryHandle( _assetConfig.cacheNamespace, { create: true } );
|
|
61
|
+
const storage = openInlineStorage( { namespace: _assetConfig.cacheNamespace, root } );
|
|
62
|
+
await migrateIndexedDB( storage ).catch( () => {} );
|
|
63
|
+
return new DownloadCache( storage );
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
} catch {
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
req.onupgradeneeded = () => req.result.createObjectStore( IDB_STORE );
|
|
61
|
-
req.onsuccess = () => resolve( req.result );
|
|
62
|
-
req.onerror = () => reject( req.error );
|
|
67
|
+
return new DownloadCache( null );
|
|
63
68
|
|
|
64
|
-
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
} )();
|
|
72
|
+
|
|
73
|
+
return downloads;
|
|
65
74
|
|
|
66
75
|
}
|
|
67
76
|
|
|
68
|
-
|
|
77
|
+
// Earlier builds kept models in IndexedDB: move them across once, then drop the database.
|
|
78
|
+
async function migrateIndexedDB( storage ) {
|
|
69
79
|
|
|
70
|
-
|
|
80
|
+
const name = `${_assetConfig.cacheNamespace}:ai-upscaler-models`;
|
|
81
|
+
if ( typeof indexedDB === 'undefined' || ! indexedDB.databases ) return;
|
|
82
|
+
if ( ! ( await indexedDB.databases() ).some( ( d ) => d.name === name ) ) return;
|
|
71
83
|
|
|
72
|
-
|
|
73
|
-
return await new Promise( ( resolve, reject ) => {
|
|
84
|
+
const entries = await new Promise( ( resolve, reject ) => {
|
|
74
85
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
req.onerror = () => reject( req.error );
|
|
86
|
+
const open = indexedDB.open( name );
|
|
87
|
+
open.onerror = () => reject( open.error );
|
|
88
|
+
open.onsuccess = () => {
|
|
79
89
|
|
|
80
|
-
|
|
90
|
+
const db = open.result;
|
|
91
|
+
if ( ! db.objectStoreNames.contains( 'models' ) ) {
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
db.close();
|
|
94
|
+
resolve( [] );
|
|
95
|
+
return;
|
|
83
96
|
|
|
84
|
-
|
|
97
|
+
}
|
|
85
98
|
|
|
86
|
-
|
|
99
|
+
const out = [];
|
|
100
|
+
const cursor = db.transaction( 'models', 'readonly' ).objectStore( 'models' ).openCursor();
|
|
101
|
+
cursor.onerror = () => reject( cursor.error );
|
|
102
|
+
cursor.onsuccess = () => {
|
|
87
103
|
|
|
88
|
-
|
|
104
|
+
const c = cursor.result;
|
|
105
|
+
if ( ! c ) {
|
|
89
106
|
|
|
90
|
-
|
|
107
|
+
db.close();
|
|
108
|
+
resolve( out );
|
|
109
|
+
return;
|
|
91
110
|
|
|
92
|
-
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
out.push( [ c.key, c.value ] );
|
|
114
|
+
c.continue();
|
|
93
115
|
|
|
94
|
-
|
|
95
|
-
await new Promise( ( resolve, reject ) => {
|
|
116
|
+
};
|
|
96
117
|
|
|
97
|
-
|
|
98
|
-
tx.objectStore( IDB_STORE ).put( buffer, url );
|
|
99
|
-
tx.oncomplete = () => resolve();
|
|
100
|
-
tx.onerror = () => reject( tx.error );
|
|
118
|
+
};
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
} );
|
|
121
|
+
|
|
122
|
+
const area = storage.area( 'downloads' );
|
|
123
|
+
for ( const [ url, buffer ] of entries ) {
|
|
103
124
|
|
|
104
|
-
|
|
125
|
+
if ( typeof url !== 'string' || ! ( buffer instanceof ArrayBuffer ) || await area.has( url ) ) continue;
|
|
126
|
+
const writer = await area.create( url, { label: url.split( '/' ).pop(), expectedBytes: buffer.byteLength } );
|
|
127
|
+
if ( ! writer ) continue;
|
|
128
|
+
await writer.write( 'data', buffer, { transfer: true } );
|
|
129
|
+
await writer.commit( { url, contentType: 'application/octet-stream', checkedAt: Date.now() } );
|
|
105
130
|
|
|
106
|
-
// Cache write failure is non-fatal
|
|
107
131
|
}
|
|
108
132
|
|
|
133
|
+
await new Promise( ( resolve ) => {
|
|
134
|
+
|
|
135
|
+
const request = indexedDB.deleteDatabase( name );
|
|
136
|
+
request.onsuccess = request.onerror = request.onblocked = () => resolve();
|
|
137
|
+
|
|
138
|
+
} );
|
|
139
|
+
|
|
109
140
|
}
|
|
110
141
|
|
|
111
142
|
// ─── Model Loading ───────────────────────────────────────────────────────────
|
|
112
143
|
|
|
113
144
|
async function fetchModel( url ) {
|
|
114
145
|
|
|
115
|
-
|
|
116
|
-
const cached = await getCachedModel( url );
|
|
117
|
-
if ( cached ) {
|
|
146
|
+
const { file, release, fromCache } = await ( await getDownloads() ).fetch( url );
|
|
118
147
|
|
|
119
|
-
|
|
120
|
-
return cached;
|
|
148
|
+
try {
|
|
121
149
|
|
|
122
|
-
|
|
150
|
+
const buffer = await file.arrayBuffer();
|
|
151
|
+
if ( fromCache ) console.log( `AI Upscaler Worker: model loaded from cache (${( buffer.byteLength / 1024 / 1024 ).toFixed( 1 )}MB)` );
|
|
152
|
+
return buffer;
|
|
123
153
|
|
|
124
|
-
|
|
125
|
-
const response = await fetch( url );
|
|
126
|
-
if ( ! response.ok ) throw new Error( `Failed to fetch model: ${response.status}` );
|
|
127
|
-
const buffer = await response.arrayBuffer();
|
|
154
|
+
} finally {
|
|
128
155
|
|
|
129
|
-
|
|
130
|
-
cacheModel( url, buffer.slice( 0 ) );
|
|
156
|
+
release();
|
|
131
157
|
|
|
132
|
-
|
|
158
|
+
}
|
|
133
159
|
|
|
134
160
|
}
|
|
135
161
|
|
|
@@ -219,6 +245,7 @@ self.onmessage = async ( e ) => {
|
|
|
219
245
|
if ( e.data.ortRuntimeUrl ) _assetConfig.ortRuntimeUrl = e.data.ortRuntimeUrl;
|
|
220
246
|
if ( e.data.ortWasmPaths ) _assetConfig.ortWasmPaths = e.data.ortWasmPaths;
|
|
221
247
|
if ( e.data.cacheNamespace ) _assetConfig.cacheNamespace = e.data.cacheNamespace;
|
|
248
|
+
if ( e.data.storage !== undefined ) _assetConfig.storage = e.data.storage;
|
|
222
249
|
await loadModel( e.data.url, e.data.sessionOptions );
|
|
223
250
|
|
|
224
251
|
} else if ( type === 'infer' ) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BVHBuilder } from '../BVHBuilder.js';
|
|
2
|
+
import { foldLeaves } from '../BVHLeafFold.js';
|
|
2
3
|
import { createLogger, fmt, applyWorkerLogLevel } from '../../utils/Logger.js';
|
|
3
4
|
|
|
4
5
|
const log = createLogger( 'bvh' );
|
|
@@ -133,7 +134,7 @@ function handleAssemble( data ) {
|
|
|
133
134
|
const {
|
|
134
135
|
topFlatData, topNodeCount, frontierMap, subtreeResults,
|
|
135
136
|
sharedTriangleData, sharedIndices, sharedReorderBuffer,
|
|
136
|
-
triangleCount
|
|
137
|
+
triangleCount, foldLeaves: fold
|
|
137
138
|
} = data;
|
|
138
139
|
|
|
139
140
|
try {
|
|
@@ -142,20 +143,22 @@ function handleAssemble( data ) {
|
|
|
142
143
|
const builder = new BVHBuilder();
|
|
143
144
|
|
|
144
145
|
// Assemble the final BVH
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
);
|
|
146
|
+
const assembled = builder.assembleParallelBVH( topFlatData, topNodeCount, frontierMap, subtreeResults );
|
|
147
|
+
const bvhData = fold ? foldLeaves( assembled ) : assembled;
|
|
148
148
|
|
|
149
|
-
// Reorder triangles using final indices from SharedArrayBuffer
|
|
150
149
|
const indices = new Uint32Array( sharedIndices );
|
|
151
|
-
|
|
152
|
-
const dst = new Uint32Array( sharedReorderBuffer );
|
|
150
|
+
if ( sharedReorderBuffer ) {
|
|
153
151
|
|
|
154
|
-
|
|
152
|
+
const src = new Uint32Array( sharedTriangleData );
|
|
153
|
+
const dst = new Uint32Array( sharedReorderBuffer );
|
|
154
|
+
|
|
155
|
+
for ( let i = 0; i < triangleCount; i ++ ) {
|
|
156
|
+
|
|
157
|
+
const srcOff = indices[ i ] * FPT;
|
|
158
|
+
const dstOff = i * FPT;
|
|
159
|
+
dst.set( src.subarray( srcOff, srcOff + FPT ), dstOff );
|
|
155
160
|
|
|
156
|
-
|
|
157
|
-
const dstOff = i * FPT;
|
|
158
|
-
dst.set( src.subarray( srcOff, srcOff + FPT ), dstOff );
|
|
161
|
+
}
|
|
159
162
|
|
|
160
163
|
}
|
|
161
164
|
|
|
@@ -190,7 +193,7 @@ function handleAssemble( data ) {
|
|
|
190
193
|
|
|
191
194
|
function handleFullBuild( data ) {
|
|
192
195
|
|
|
193
|
-
const { triangleData, triangleByteOffset, triangleByteLength, depth, reportProgress, treeletOptimization, reinsertionOptimization, sharedReorderBuffer, maxLeafSize, numBins, maxBins, minBins } = data;
|
|
196
|
+
const { triangleData, triangleByteOffset, triangleByteLength, depth, reportProgress, treeletOptimization, reinsertionOptimization, sharedReorderBuffer, maxLeafSize, numBins, maxBins, minBins, foldLeaves: fold } = data;
|
|
194
197
|
const builder = new BVHBuilder();
|
|
195
198
|
// Honor the build params forwarded from SceneProcessor (previously ignored → default leaf 8).
|
|
196
199
|
if ( maxLeafSize !== undefined ) builder.maxLeafSize = maxLeafSize;
|
|
@@ -229,7 +232,8 @@ function handleFullBuild( data ) {
|
|
|
229
232
|
const bvhRoot = builder.buildSync( inputTriangles, depth, progressCallback, reorderTarget );
|
|
230
233
|
|
|
231
234
|
const flattenStart = performance.now();
|
|
232
|
-
const
|
|
235
|
+
const flat = builder.flattenBVH( bvhRoot );
|
|
236
|
+
const bvhData = fold ? foldLeaves( flat ) : flat;
|
|
233
237
|
const flattenTime = performance.now() - flattenStart;
|
|
234
238
|
log.debug( `flatten ${fmt.ms( flattenTime )} (${fmt.mb( bvhData.byteLength )})` );
|
|
235
239
|
|