@@ -4,94 +4,106 @@ export default class TimestampQueryManager {
44 // class does nothing.
55 timestampSupported : boolean ;
66
7- // Number of timestamp counters
8- timestampCount : number ;
9-
107 // The query objects. This is meant to be used in a ComputePassDescriptor's
118 // or RenderPassDescriptor's 'timestampWrites' field.
12- timestampQuerySet : GPUQuerySet ;
9+ # timestampQuerySet: GPUQuerySet ;
1310
1411 // A buffer where to store query results
15- timestampBuffer : GPUBuffer ;
12+ # timestampBuffer: GPUBuffer ;
1613
1714 // A buffer to map this result back to CPU
18- timestampMapBuffer : GPUBuffer ;
15+ # timestampMapBuffer: GPUBuffer ;
1916
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 ;
2219
2320 // Device must have the "timestamp-query" feature
24- constructor ( device : GPUDevice , timestampCount : number ) {
21+ constructor ( device : GPUDevice ) {
2522 this . timestampSupported = device . features . has ( 'timestamp-query' ) ;
2623 if ( ! this . timestampSupported ) return ;
2724
28- this . timestampCount = timestampCount ;
25+ this . passDurationMeasurementNs = 0 ;
2926
3027 // Create timestamp queries
31- this . timestampQuerySet = device . createQuerySet ( {
28+ this . # timestampQuerySet = device . createQuerySet ( {
3229 type : 'timestamp' ,
33- count : timestampCount , // begin and end
30+ count : 2 , // begin and end
3431 } ) ;
3532
3633 // Create a buffer where to store the result of GPU queries
3734 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 ,
4137 usage : GPUBufferUsage . COPY_SRC | GPUBufferUsage . QUERY_RESOLVE ,
4238 } ) ;
4339
4440 // 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 ,
4743 usage : GPUBufferUsage . COPY_DST | GPUBufferUsage . MAP_READ ,
4844 } ) ;
45+ }
4946
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 ;
5160 }
5261
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 ) {
5564 if ( ! this . timestampSupported ) return ;
5665
5766 // After the end of the measured render pass, we resolve queries into a
5867 // dedicated buffer.
5968 commandEncoder . resolveQuerySet (
60- this . timestampQuerySet ,
69+ this . # timestampQuerySet,
6170 0 /* firstQuery */ ,
62- this . timestampCount /* queryCount */ ,
63- this . timestampBuffer ,
71+ this . #timestampQuerySet . count /* queryCount */ ,
72+ this . # timestampBuffer,
6473 0 /* destinationOffset */
6574 ) ;
6675
67- if ( ! this . hasOngoingTimestampReadback ) {
68- // Copy values to the mapped buffer
76+ if ( this . #timestampMapBuffer . mapState === 'unmapped' ) {
77+ // Copy values to the mappable buffer
6978 commandEncoder . copyBufferToBuffer (
70- this . timestampBuffer ,
79+ this . # timestampBuffer,
7180 0 ,
72- this . timestampMapBuffer ,
81+ this . # timestampMapBuffer,
7382 0 ,
74- this . timestampBuffer . size
83+ this . # timestampBuffer. size
7584 ) ;
7685 }
7786 }
7887
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 {
8190 if ( ! this . timestampSupported ) return ;
82- if ( this . hasOngoingTimestampReadback ) return ;
91+ if ( this . #timestampMapBuffer . mapState !== 'unmapped' ) return ;
8392
84- this . hasOngoingTimestampReadback = true ;
85-
86- const buffer = this . timestampMapBuffer ;
93+ const buffer = this . #timestampMapBuffer;
8794 void buffer . mapAsync ( GPUMapMode . READ ) . then ( ( ) => {
8895 const rawData = buffer . getMappedRange ( ) ;
8996 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+ }
93106 buffer . unmap ( ) ;
94- this . hasOngoingTimestampReadback = false ;
95107 } ) ;
96108 }
97109}
0 commit comments