@@ -4,94 +4,106 @@ export default class TimestampQueryManager {
4
4
// class does nothing.
5
5
timestampSupported : boolean ;
6
6
7
- // Number of timestamp counters
8
- timestampCount : number ;
9
-
10
7
// The query objects. This is meant to be used in a ComputePassDescriptor's
11
8
// or RenderPassDescriptor's 'timestampWrites' field.
12
- timestampQuerySet : GPUQuerySet ;
9
+ # timestampQuerySet: GPUQuerySet ;
13
10
14
11
// A buffer where to store query results
15
- timestampBuffer : GPUBuffer ;
12
+ # timestampBuffer: GPUBuffer ;
16
13
17
14
// A buffer to map this result back to CPU
18
- timestampMapBuffer : GPUBuffer ;
15
+ # timestampMapBuffer: GPUBuffer ;
19
16
20
- // State used to avoid firing concurrent readback of timestamp values
21
- hasOngoingTimestampReadback : boolean ;
17
+ // Last queried elapsed time of the pass in nanoseconds.
18
+ passDurationMeasurementNs : number ;
22
19
23
20
// Device must have the "timestamp-query" feature
24
- constructor ( device : GPUDevice , timestampCount : number ) {
21
+ constructor ( device : GPUDevice ) {
25
22
this . timestampSupported = device . features . has ( 'timestamp-query' ) ;
26
23
if ( ! this . timestampSupported ) return ;
27
24
28
- this . timestampCount = timestampCount ;
25
+ this . passDurationMeasurementNs = 0 ;
29
26
30
27
// Create timestamp queries
31
- this . timestampQuerySet = device . createQuerySet ( {
28
+ this . # timestampQuerySet = device . createQuerySet ( {
32
29
type : 'timestamp' ,
33
- count : timestampCount , // begin and end
30
+ count : 2 , // begin and end
34
31
} ) ;
35
32
36
33
// Create a buffer where to store the result of GPU queries
37
34
const timestampByteSize = 8 ; // timestamps are uint64
38
- const timestampBufferSize = timestampCount * timestampByteSize ;
39
- this . timestampBuffer = device . createBuffer ( {
40
- size : timestampBufferSize ,
35
+ this . #timestampBuffer = device . createBuffer ( {
36
+ size : this . #timestampQuerySet. count * timestampByteSize ,
41
37
usage : GPUBufferUsage . COPY_SRC | GPUBufferUsage . QUERY_RESOLVE ,
42
38
} ) ;
43
39
44
40
// Create a buffer to map the result back to the CPU
45
- this . timestampMapBuffer = device . createBuffer ( {
46
- size : timestampBufferSize ,
41
+ this . # timestampMapBuffer = device . createBuffer ( {
42
+ size : this . #timestampBuffer . size ,
47
43
usage : GPUBufferUsage . COPY_DST | GPUBufferUsage . MAP_READ ,
48
44
} ) ;
45
+ }
49
46
50
- this . hasOngoingTimestampReadback = false ;
47
+ // Add both a start and end timestamp.
48
+ addTimestampWrite (
49
+ passDescriptor : GPURenderPassDescriptor | GPUComputePassDescriptor
50
+ ) {
51
+ if ( this . timestampSupported ) {
52
+ // We instruct the render pass to write to the timestamp query before/after
53
+ passDescriptor . timestampWrites = {
54
+ querySet : this . #timestampQuerySet,
55
+ beginningOfPassWriteIndex : 0 ,
56
+ endOfPassWriteIndex : 1 ,
57
+ } ;
58
+ }
59
+ return passDescriptor ;
51
60
}
52
61
53
- // Resolve all timestamp queries and copy the result into the map buffer
54
- resolveAll ( commandEncoder : GPUCommandEncoder ) {
62
+ // Resolve the timestamp queries and copy the result into the mappable buffer if possible.
63
+ resolve ( commandEncoder : GPUCommandEncoder ) {
55
64
if ( ! this . timestampSupported ) return ;
56
65
57
66
// After the end of the measured render pass, we resolve queries into a
58
67
// dedicated buffer.
59
68
commandEncoder . resolveQuerySet (
60
- this . timestampQuerySet ,
69
+ this . # timestampQuerySet,
61
70
0 /* firstQuery */ ,
62
- this . timestampCount /* queryCount */ ,
63
- this . timestampBuffer ,
71
+ this . #timestampQuerySet . count /* queryCount */ ,
72
+ this . # timestampBuffer,
64
73
0 /* destinationOffset */
65
74
) ;
66
75
67
- if ( ! this . hasOngoingTimestampReadback ) {
68
- // Copy values to the mapped buffer
76
+ if ( this . #timestampMapBuffer . mapState === 'unmapped' ) {
77
+ // Copy values to the mappable buffer
69
78
commandEncoder . copyBufferToBuffer (
70
- this . timestampBuffer ,
79
+ this . # timestampBuffer,
71
80
0 ,
72
- this . timestampMapBuffer ,
81
+ this . # timestampMapBuffer,
73
82
0 ,
74
- this . timestampBuffer . size
83
+ this . # timestampBuffer. size
75
84
) ;
76
85
}
77
86
}
78
87
79
- // Once resolved, we can read back the value of timestamps
80
- readAsync ( onTimestampReadBack : ( timestamps : BigUint64Array ) => void ) : void {
88
+ // Read the values of timestamps.
89
+ tryInitiateTimestampDownload ( ) : void {
81
90
if ( ! this . timestampSupported ) return ;
82
- if ( this . hasOngoingTimestampReadback ) return ;
91
+ if ( this . #timestampMapBuffer . mapState !== 'unmapped' ) return ;
83
92
84
- this . hasOngoingTimestampReadback = true ;
85
-
86
- const buffer = this . timestampMapBuffer ;
93
+ const buffer = this . #timestampMapBuffer;
87
94
void buffer . mapAsync ( GPUMapMode . READ ) . then ( ( ) => {
88
95
const rawData = buffer . getMappedRange ( ) ;
89
96
const timestamps = new BigUint64Array ( rawData ) ;
90
-
91
- onTimestampReadBack ( timestamps ) ;
92
-
97
+ // Subtract the begin time from the end time.
98
+ // Cast into number. Number can be 9007199254740991 as max integer
99
+ // which is 109 days of nano seconds.
100
+ const elapsedNs = Number ( timestamps [ 1 ] - timestamps [ 0 ] ) ;
101
+ // It's possible elapsedNs is negative which means it's invalid
102
+ // (see spec https://gpuweb.github.io/gpuweb/#timestamp)
103
+ if ( elapsedNs >= 0 ) {
104
+ this . passDurationMeasurementNs = elapsedNs ;
105
+ }
93
106
buffer . unmap ( ) ;
94
- this . hasOngoingTimestampReadback = false ;
95
107
} ) ;
96
108
}
97
109
}
0 commit comments