Skip to content

Commit

Permalink
[js/webgpu] Change timestamp-query-in-passes to timestamp-query (micr…
Browse files Browse the repository at this point in the history
…osoft#18108)

Timestamp-query has a broader support than timestamp-query-in-passes on
all the platforms, including macOS.
Note that to enable timestamp-query, you still need to add switch
"--enable-dawn-features=allow_unsafe_apis" to Chrome. By default, the
lowest 16 bits are masked with 0 (at a granularity about 0.1ms) for
privacy. To get the highest precision, you need to add another switch
"--enable-webgpu-developer-features".
  • Loading branch information
Yang Gu authored Oct 26, 2023
1 parent 453902f commit c1b70c3
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 48 deletions.
60 changes: 37 additions & 23 deletions web/lib/wasm/jsep/backend-webgpu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,14 @@ export class WebGpuBackend {
*/
kernels: Map<number, [string, string, RunFunction, [((attribute: unknown) => unknown) | undefined, unknown]]>;

commandEncoder: GPUCommandEncoder|null = null;
computePassEncoder: GPUComputePassEncoder|null = null;
private commandEncoder: GPUCommandEncoder|null = null;
private computePassEncoder: GPUComputePassEncoder|null = null;
pendingDispatchNumber = 0;

supportTimestampQuery = false;
profilingQuerySet: GPUQuerySet;
profilingQueryData: GpuData;
profilingTimeBase?: bigint;
queryData?: GpuData;
querySet?: GPUQuerySet;
querySetCount = 2;
queryTimeBase?: bigint;

env: Env;

Expand Down Expand Up @@ -168,11 +168,9 @@ export class WebGpuBackend {
},
requiredFeatures,
};
// WebGPU Spec: Timestamp Queries Inside Passes
// https://github.com/gpuweb/gpuweb/blob/main/proposals/timestamp-query-inside-passes.md
if (adapter.features.has('timestamp-query-inside-passes')) {
this.supportTimestampQuery = true;
requiredFeatures.push('timestamp-query-inside-passes' as GPUFeatureName);

if (adapter.features.has('timestamp-query')) {
requiredFeatures.push('timestamp-query');
}
if (adapter.features.has('shader-f16')) {
requiredFeatures.push('shader-f16');
Expand All @@ -197,21 +195,14 @@ export class WebGpuBackend {
}
};

if (this.supportTimestampQuery) {
this.profilingQuerySet = this.device.createQuerySet({
type: 'timestamp',
count: 2,
});
}

Object.defineProperty(this.env.webgpu, 'device', {value: this.device});
}

dispose(): void {
// currently, we do not do anything in this function. In all known use cases, we don't have the requirement to
// actually dispose the WebGpuBackend instance, because it's always used as a singleton.
//
// revisit this place if we get real requirement to dispose the instance.
if (typeof this.querySet !== 'undefined') {
this.querySet.destroy();
}
this.gpuDataManager.dispose();
}

getCommandEncoder(): GPUCommandEncoder {
Expand All @@ -223,7 +214,22 @@ export class WebGpuBackend {

getComputePassEncoder(): GPUComputePassEncoder {
if (!this.computePassEncoder) {
this.computePassEncoder = this.getCommandEncoder().beginComputePass();
const computePassDescriptor: GPUComputePassDescriptor = {};
if (this.isQueryEnabled()) {
if (typeof this.querySet === 'undefined') {
this.querySet = this.device.createQuerySet({
type: 'timestamp',
count: this.querySetCount,
});
}
computePassDescriptor.timestampWrites = {
querySet: this.querySet,
beginningOfPassWriteIndex: 0,
endOfPassWriteIndex: 1,
};
}

this.computePassEncoder = this.getCommandEncoder().beginComputePass(computePassDescriptor);
}
return this.computePassEncoder;
}
Expand All @@ -245,6 +251,14 @@ export class WebGpuBackend {
}
}

isQueryEnabled(): boolean {
if (this.device.features.has('timestamp-query') && this.env.webgpu.profilingMode === 'default') {
return true;
} else {
return false;
}
}

/**
* run a WebGPU program.
* @param program a ProgramInfo instance
Expand Down
38 changes: 13 additions & 25 deletions web/lib/wasm/jsep/webgpu/program-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,6 @@ export class ProgramManager {
const device = this.backend.device;

const computePassEncoder = this.backend.getComputePassEncoder();
const profilingEnabled = this.backend.supportTimestampQuery && this.backend.env.webgpu.profilingMode === 'default';
if (profilingEnabled) {
// profiling write start timestamp

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(computePassEncoder as any).writeTimestamp(this.backend.profilingQuerySet, 0);
}

computePassEncoder.setPipeline(buildArtifact.computePipeline);
const entries = [];
for (const input of inputs) {
Expand All @@ -65,24 +57,20 @@ export class ProgramManager {

this.backend.pendingDispatchNumber++;

if (profilingEnabled) {
// profiling write end timestamp

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(computePassEncoder as any).writeTimestamp(this.backend.profilingQuerySet, 1);
if (this.backend.profilingQueryData == null) {
this.backend.profilingQueryData =
if (this.backend.isQueryEnabled()) {
if (typeof this.backend.queryData === 'undefined') {
this.backend.queryData = this.backend.gpuDataManager.create(
// eslint-disable-next-line no-bitwise
this.backend.gpuDataManager.create(16, GPUBufferUsage.COPY_SRC | GPUBufferUsage.QUERY_RESOLVE);
this.backend.querySetCount * 8, GPUBufferUsage.COPY_SRC | GPUBufferUsage.QUERY_RESOLVE);
}
// eslint-disable-next-line no-bitwise
const syncData = this.backend.gpuDataManager.create(16, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST);
const syncData = this.backend.gpuDataManager.create(
// eslint-disable-next-line no-bitwise
this.backend.querySetCount * 8, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST);

this.backend.endComputePass();
this.backend.getCommandEncoder().resolveQuerySet(
this.backend.profilingQuerySet, 0, 2, this.backend.profilingQueryData.buffer, 0);
this.backend.getCommandEncoder().resolveQuerySet(this.backend.querySet, 0, 2, this.backend.queryData.buffer, 0);
this.backend.getCommandEncoder().copyBufferToBuffer(
this.backend.profilingQueryData.buffer, 0, syncData.buffer, 0, 16);
this.backend.queryData.buffer, 0, syncData.buffer, 0, this.backend.querySetCount * 8);
this.backend.flush();

const kernelId = this.backend.currentKernelId!;
Expand All @@ -96,12 +84,12 @@ export class ProgramManager {

syncData.buffer.unmap();

if (typeof this.backend.profilingTimeBase === 'undefined') {
this.backend.profilingTimeBase = startTimeU64;
if (typeof this.backend.queryTimeBase === 'undefined') {
this.backend.queryTimeBase = startTimeU64;
}

const startTime = Number(startTimeU64 - this.backend.profilingTimeBase);
const endTime = Number(endTimeU64 - this.backend.profilingTimeBase);
const startTime = Number(startTimeU64 - this.backend.queryTimeBase);
const endTime = Number(endTimeU64 - this.backend.queryTimeBase);

if (!Number.isSafeInteger(startTime) || !Number.isSafeInteger(endTime)) {
throw new RangeError('incorrect timestamp range');
Expand Down

0 comments on commit c1b70c3

Please sign in to comment.