16
16
17
17
import { debounce } from "lodash-es" ;
18
18
import {
19
- WithParameters ,
20
- withChunkManager ,
21
19
Chunk ,
22
20
ChunkSource ,
21
+ WithParameters ,
22
+ withChunkManager ,
23
23
} from "#src/chunk_manager/backend.js" ;
24
24
import { ChunkPriorityTier , ChunkState } from "#src/chunk_manager/base.js" ;
25
+ import type { CredentialsProvider } from "#src/credentials_provider/index.js" ;
25
26
import { WithSharedCredentialsProviderCounterpart } from "#src/credentials_provider/shared_counterpart.js" ;
26
27
import type { ChunkedGraphChunkSpecification } from "#src/datasource/graphene/base.js" ;
27
28
import {
28
- getGrapheneFragmentKey ,
29
- GRAPHENE_MESH_NEW_SEGMENT_RPC_ID ,
30
- responseIdentity ,
31
- ChunkedGraphSourceParameters ,
32
- MeshSourceParameters ,
33
29
CHUNKED_GRAPH_LAYER_RPC_ID ,
34
30
CHUNKED_GRAPH_RENDER_LAYER_UPDATE_SOURCES_RPC_ID ,
31
+ ChunkedGraphSourceParameters ,
32
+ GRAPHENE_MESH_NEW_SEGMENT_RPC_ID ,
33
+ MeshSourceParameters ,
35
34
RENDER_RATIO_LIMIT ,
35
+ getGrapheneFragmentKey ,
36
36
isBaseSegmentId ,
37
37
} from "#src/datasource/graphene/base.js" ;
38
38
import { decodeManifestChunk } from "#src/datasource/precomputed/backend.js" ;
39
39
import type { FragmentChunk , ManifestChunk } from "#src/mesh/backend.js" ;
40
- import { assignMeshFragmentData , MeshSource } from "#src/mesh/backend.js" ;
40
+ import { MeshSource , assignMeshFragmentData } from "#src/mesh/backend.js" ;
41
41
import { decodeDraco } from "#src/mesh/draco/index.js" ;
42
42
import type { DisplayDimensionRenderInfo } from "#src/navigation_state.js" ;
43
43
import type {
44
- RenderedViewBackend ,
45
44
RenderLayerBackendAttachment ,
45
+ RenderedViewBackend ,
46
46
} from "#src/render_layer_backend.js" ;
47
47
import { RenderLayerBackend } from "#src/render_layer_backend.js" ;
48
48
import { withSegmentationLayerBackendState } from "#src/segmentation_display_state/backend.js" ;
@@ -51,8 +51,8 @@ import type { SharedWatchableValue } from "#src/shared_watchable_value.js";
51
51
import type { SliceViewChunkSourceBackend } from "#src/sliceview/backend.js" ;
52
52
import { deserializeTransformedSources } from "#src/sliceview/backend.js" ;
53
53
import type {
54
- TransformedSource ,
55
54
SliceViewProjectionParameters ,
55
+ TransformedSource ,
56
56
} from "#src/sliceview/base.js" ;
57
57
import {
58
58
forEachPlaneIntersectingVolumetricChunk ,
@@ -61,9 +61,13 @@ import {
61
61
import { computeChunkBounds } from "#src/sliceview/volume/backend.js" ;
62
62
import { Uint64Set } from "#src/uint64_set.js" ;
63
63
import { fetchSpecialHttpByteRange } from "#src/util/byte_range_http_requests.js" ;
64
- import type { CancellationToken } from "#src/util/cancellation.js" ;
64
+ import {
65
+ CancellationTokenSource ,
66
+ type CancellationToken ,
67
+ } from "#src/util/cancellation.js" ;
65
68
import { vec3 , vec3Key } from "#src/util/geom.js" ;
66
69
import { responseArrayBuffer , responseJson } from "#src/util/http_request.js" ;
70
+ import { Signal } from "#src/util/signal.js" ;
67
71
import type {
68
72
SpecialProtocolCredentials ,
69
73
SpecialProtocolCredentialsProvider ,
@@ -76,7 +80,7 @@ import {
76
80
withSharedVisibility ,
77
81
} from "#src/visibility_priority/backend.js" ;
78
82
import type { RPC } from "#src/worker_rpc.js" ;
79
- import { registerSharedObject , registerRPC } from "#src/worker_rpc.js" ;
83
+ import { registerRPC , registerSharedObject } from "#src/worker_rpc.js" ;
80
84
81
85
function getVerifiedFragmentPromise (
82
86
credentialsProvider : SpecialProtocolCredentialsProvider ,
@@ -260,6 +264,73 @@ function decodeChunkedGraphChunk(leaves: string[]) {
260
264
return final ;
261
265
}
262
266
267
+ class LeavesManyProxy {
268
+ pendingRequests = new Map <
269
+ string ,
270
+ [ Signal < ( response : any ) => void > , Uint64Set , CancellationTokenSource ]
271
+ > ( ) ;
272
+
273
+ constructor (
274
+ private parameters : ChunkedGraphSourceParameters ,
275
+ private credentialsProvider ?: CredentialsProvider < any > ,
276
+ ) { }
277
+
278
+ async request (
279
+ segment : Uint64 ,
280
+ bounds : string ,
281
+ cancellationToken : CancellationToken ,
282
+ ) : Promise < any > {
283
+ const { pendingRequests } = this ;
284
+ let pendingRequest = pendingRequests . get ( bounds ) ;
285
+ if ( ! pendingRequest ) {
286
+ const { parameters, credentialsProvider } = this ;
287
+ const signal = new Signal < ( request : any ) => void > ( ) ;
288
+ const requestCancellationToken = new CancellationTokenSource ( ) ;
289
+ const segments = new Uint64Set ( ) ;
290
+ pendingRequest = [ signal , segments , requestCancellationToken ] ;
291
+ pendingRequests . set ( bounds , pendingRequest ) ;
292
+ setTimeout ( async ( ) => {
293
+ pendingRequests . delete ( bounds ) ;
294
+ try {
295
+ const response = await cancellableFetchSpecialOk (
296
+ credentialsProvider ,
297
+ `${ parameters . url } /leaves_many?int64_as_str=1&bounds=${ bounds } ` ,
298
+ {
299
+ method : "POST" ,
300
+ body : JSON . stringify ( {
301
+ node_ids : [ ...segments ] ,
302
+ } ) ,
303
+ } ,
304
+ responseJson ,
305
+ requestCancellationToken ,
306
+ ) ;
307
+ signal . dispatch ( response ) ;
308
+ } catch ( e ) {
309
+ signal . dispatch ( e ) ;
310
+ }
311
+ } , 0 ) ;
312
+ }
313
+ const [ request , segments , requestCancellationToken ] = pendingRequest ;
314
+ segments . add ( segment ) ;
315
+ cancellationToken . add ( ( ) => {
316
+ segments . delete ( segment ) ;
317
+ if ( segments . size === 0 ) {
318
+ requestCancellationToken . cancel ( ) ;
319
+ }
320
+ } ) ;
321
+ return new Promise ( ( f , r ) => {
322
+ const unregister = request . add ( ( response ) => {
323
+ unregister ( ) ;
324
+ if ( response instanceof Error ) {
325
+ r ( response ) ;
326
+ } else {
327
+ f ( response [ segment . toJSON ( ) ] ) ;
328
+ }
329
+ } ) ;
330
+ } ) ;
331
+ }
332
+ }
333
+
263
334
@registerSharedObject ( )
264
335
export class GrapheneChunkedGraphChunkSource extends WithParameters (
265
336
WithSharedCredentialsProviderCounterpart < SpecialProtocolCredentials > ( ) (
@@ -271,43 +342,37 @@ export class GrapheneChunkedGraphChunkSource extends WithParameters(
271
342
chunks : Map < string , ChunkedGraphChunk > ;
272
343
tempChunkDataSize : Uint32Array ;
273
344
tempChunkPosition : Float32Array ;
345
+ leavesManyProxy : LeavesManyProxy ;
274
346
275
347
constructor ( rpc : RPC , options : any ) {
276
348
super ( rpc , options ) ;
277
349
this . spec = options . spec ;
278
350
const rank = this . spec . rank ;
279
351
this . tempChunkDataSize = new Uint32Array ( rank ) ;
280
352
this . tempChunkPosition = new Float32Array ( rank ) ;
353
+ this . leavesManyProxy = new LeavesManyProxy (
354
+ this . parameters ,
355
+ this . credentialsProvider ,
356
+ ) ;
281
357
}
282
358
283
359
async download (
284
360
chunk : ChunkedGraphChunk ,
285
361
cancellationToken : CancellationToken ,
286
362
) : Promise < void > {
287
- const { parameters } = this ;
288
363
const chunkPosition = this . computeChunkBounds ( chunk ) ;
289
364
const chunkDataSize = chunk . chunkDataSize ! ;
290
365
const bounds =
291
366
`${ chunkPosition [ 0 ] } -${ chunkPosition [ 0 ] + chunkDataSize [ 0 ] } _` +
292
367
`${ chunkPosition [ 1 ] } -${ chunkPosition [ 1 ] + chunkDataSize [ 1 ] } _` +
293
368
`${ chunkPosition [ 2 ] } -${ chunkPosition [ 2 ] + chunkDataSize [ 2 ] } ` ;
294
369
295
- const request = cancellableFetchSpecialOk (
296
- this . credentialsProvider ,
297
- `${ parameters . url } /${ chunk . segment } /leaves?int64_as_str=1&bounds=${ bounds } ` ,
298
- { } ,
299
- responseIdentity ,
370
+ const request = await this . leavesManyProxy . request (
371
+ chunk . segment ,
372
+ bounds ,
300
373
cancellationToken ,
301
374
) ;
302
- await this . withErrorMessage (
303
- request ,
304
- `Fetching leaves of segment ${ chunk . segment } in region ${ bounds } : ` ,
305
- )
306
- . then ( ( res ) => res . json ( ) )
307
- . then ( ( res ) => {
308
- chunk . leaves = decodeChunkedGraphChunk ( res . leaf_ids ) ;
309
- } )
310
- . catch ( ( err ) => console . error ( err ) ) ;
375
+ chunk . leaves = decodeChunkedGraphChunk ( request ) ;
311
376
}
312
377
313
378
getChunk ( chunkGridPosition : Float32Array , segment : Uint64 ) {
@@ -325,23 +390,6 @@ export class GrapheneChunkedGraphChunkSource extends WithParameters(
325
390
computeChunkBounds ( chunk : ChunkedGraphChunk ) {
326
391
return computeChunkBounds ( this , chunk ) ;
327
392
}
328
-
329
- async withErrorMessage (
330
- promise : Promise < Response > ,
331
- errorPrefix : string ,
332
- ) : Promise < Response > {
333
- const response = await promise ;
334
- if ( response . ok ) {
335
- return response ;
336
- }
337
- let msg : string ;
338
- try {
339
- msg = ( await response . json ( ) ) . message ;
340
- } catch {
341
- msg = await response . text ( ) ;
342
- }
343
- throw new Error ( `[${ response . status } ] ${ errorPrefix } ${ msg } ` ) ;
344
- }
345
393
}
346
394
347
395
interface ChunkedGraphRenderLayerAttachmentState {
0 commit comments