or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

commands.mdconfiguration.mdcore-types.mdgpu-interfaces.mdindex.mdpipelines.mdrender-bundles.mdresources.md

commands.mddocs/

0

# Commands and Passes

1

2

Command recording, encoding, and execution including render passes and compute passes for GPU work submission.

3

4

## Capabilities

5

6

### GPU Command Encoder

7

8

Main interface for recording GPU commands into command buffers.

9

10

```typescript { .api }

11

interface GPUCommandEncoder {

12

/** Optional debug label */

13

readonly label: string | undefined;

14

15

/**

16

* Begins a render pass

17

* @param descriptor - Render pass configuration

18

* @returns Render pass encoder

19

*/

20

beginRenderPass(descriptor: GPURenderPassDescriptor): GPURenderPassEncoder;

21

22

/**

23

* Begins a compute pass

24

* @param descriptor - Optional compute pass configuration

25

* @returns Compute pass encoder

26

*/

27

beginComputePass(descriptor?: GPUComputePassDescriptor): GPUComputePassEncoder;

28

29

/**

30

* Copies data between buffers

31

* @param source - Source buffer

32

* @param sourceOffset - Byte offset in source

33

* @param destination - Destination buffer

34

* @param destinationOffset - Byte offset in destination

35

* @param size - Number of bytes to copy

36

*/

37

copyBufferToBuffer(

38

source: GPUBuffer,

39

sourceOffset: GPUSize64,

40

destination: GPUBuffer,

41

destinationOffset: GPUSize64,

42

size: GPUSize64

43

): void;

44

45

/**

46

* Copies buffer data to a texture

47

* @param source - Source buffer and layout

48

* @param destination - Destination texture info

49

* @param copySize - Size of the region to copy

50

*/

51

copyBufferToTexture(

52

source: GPUTexelCopyBufferInfo,

53

destination: GPUTexelCopyTextureInfo,

54

copySize: GPUExtent3D

55

): void;

56

57

/**

58

* Copies texture data to a buffer

59

* @param source - Source texture info

60

* @param destination - Destination buffer and layout

61

* @param copySize - Size of the region to copy

62

*/

63

copyTextureToBuffer(

64

source: GPUTexelCopyTextureInfo,

65

destination: GPUTexelCopyBufferInfo,

66

copySize: GPUExtent3D

67

): void;

68

69

/**

70

* Copies data between textures

71

* @param source - Source texture info

72

* @param destination - Destination texture info

73

* @param copySize - Size of the region to copy

74

*/

75

copyTextureToTexture(

76

source: GPUTexelCopyTextureInfo,

77

destination: GPUTexelCopyTextureInfo,

78

copySize: GPUExtent3D

79

): void;

80

81

/**

82

* Clears a buffer with zeros

83

* @param buffer - Buffer to clear

84

* @param offset - Byte offset (optional)

85

* @param size - Number of bytes to clear (optional)

86

*/

87

clearBuffer(buffer: GPUBuffer, offset?: GPUSize64, size?: GPUSize64): void;

88

89

/**

90

* Writes a timestamp to a query set

91

* @param querySet - Query set for timestamps

92

* @param queryIndex - Query index to write to

93

*/

94

writeTimestamp(querySet: GPUQuerySet, queryIndex: GPUSize32): void;

95

96

/**

97

* Resolves query results to a buffer

98

* @param querySet - Query set to resolve

99

* @param firstQuery - First query index

100

* @param queryCount - Number of queries to resolve

101

* @param destination - Destination buffer

102

* @param destinationOffset - Byte offset in destination

103

*/

104

resolveQuerySet(

105

querySet: GPUQuerySet,

106

firstQuery: GPUSize32,

107

queryCount: GPUSize32,

108

destination: GPUBuffer,

109

destinationOffset: GPUSize64

110

): void;

111

112

/**

113

* Finishes command recording

114

* @param descriptor - Optional command buffer configuration

115

* @returns Completed command buffer

116

*/

117

finish(descriptor?: GPUCommandBufferDescriptor): GPUCommandBuffer;

118

119

// Debug commands

120

pushDebugGroup(groupLabel: string): void;

121

popDebugGroup(): void;

122

insertDebugMarker(markerLabel: string): void;

123

}

124

125

interface GPUCommandEncoderDescriptor extends GPUObjectDescriptorBase {}

126

127

interface GPUCommandBufferDescriptor extends GPUObjectDescriptorBase {}

128

```

129

130

### GPU Command Buffer

131

132

Immutable recorded commands ready for submission to the GPU queue.

133

134

```typescript { .api }

135

interface GPUCommandBuffer {

136

/** Optional debug label */

137

readonly label: string | undefined;

138

}

139

```

140

141

### GPU Render Pass Encoder

142

143

Records rendering commands within a render pass.

144

145

```typescript { .api }

146

interface GPURenderPassEncoder extends GPURenderCommandsMixin {

147

/**

148

* Sets the viewport transformation

149

* @param x - X coordinate of viewport origin

150

* @param y - Y coordinate of viewport origin

151

* @param width - Viewport width

152

* @param height - Viewport height

153

* @param minDepth - Minimum depth value (0.0-1.0)

154

* @param maxDepth - Maximum depth value (0.0-1.0)

155

*/

156

setViewport(

157

x: number,

158

y: number,

159

width: number,

160

height: number,

161

minDepth: number,

162

maxDepth: number

163

): void;

164

165

/**

166

* Sets the scissor rectangle

167

* @param x - X coordinate of scissor origin

168

* @param y - Y coordinate of scissor origin

169

* @param width - Scissor width

170

* @param height - Scissor height

171

*/

172

setScissorRect(

173

x: GPUIntegerCoordinate,

174

y: GPUIntegerCoordinate,

175

width: GPUIntegerCoordinate,

176

height: GPUIntegerCoordinate

177

): void;

178

179

/**

180

* Begins an occlusion query

181

* @param queryIndex - Index in the query set

182

*/

183

beginOcclusionQuery(queryIndex: GPUSize32): void;

184

185

/** Ends the current occlusion query */

186

endOcclusionQuery(): void;

187

188

/** Ends the render pass */

189

end(): void;

190

191

// Inherited from GPURenderCommandsMixin

192

setPipeline(pipeline: GPURenderPipeline): void;

193

setIndexBuffer(buffer: GPUBuffer, format: GPUIndexFormat, offset?: GPUSize64, size?: GPUSize64): void;

194

setVertexBuffer(slot: GPUIndex32, buffer: GPUBuffer | null, offset?: GPUSize64, size?: GPUSize64): void;

195

draw(vertexCount: GPUSize32, instanceCount?: GPUSize32, firstVertex?: GPUSize32, firstInstance?: GPUSize32): void;

196

drawIndexed(indexCount: GPUSize32, instanceCount?: GPUSize32, firstIndex?: GPUSize32, baseVertex?: GPUSignedOffset32, firstInstance?: GPUSize32): void;

197

drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: GPUSize64): void;

198

drawIndexedIndirect(indirectBuffer: GPUBuffer, indirectOffset: GPUSize64): void;

199

200

// Inherited from GPUBindingCommandsMixin

201

setBindGroup(index: GPUIndex32, bindGroup: GPUBindGroup | null, dynamicOffsets?: Iterable<GPUBufferDynamicOffset>): void;

202

203

// Inherited from GPUDebugCommandsMixin

204

pushDebugGroup(groupLabel: string): void;

205

popDebugGroup(): void;

206

insertDebugMarker(markerLabel: string): void;

207

}

208

```

209

210

### GPU Compute Pass Encoder

211

212

Records compute commands within a compute pass.

213

214

```typescript { .api }

215

interface GPUComputePassEncoder {

216

/** Optional debug label */

217

readonly label: string | undefined;

218

219

/**

220

* Sets the compute pipeline

221

* @param pipeline - Compute pipeline to use

222

*/

223

setPipeline(pipeline: GPUComputePipeline): void;

224

225

/**

226

* Dispatches compute workgroups

227

* @param workgroupCountX - Number of workgroups in X dimension

228

* @param workgroupCountY - Number of workgroups in Y dimension (default: 1)

229

* @param workgroupCountZ - Number of workgroups in Z dimension (default: 1)

230

*/

231

dispatchWorkgroups(

232

workgroupCountX: GPUSize32,

233

workgroupCountY?: GPUSize32,

234

workgroupCountZ?: GPUSize32

235

): void;

236

237

/**

238

* Dispatches workgroups with counts from buffer

239

* @param indirectBuffer - Buffer containing dispatch parameters

240

* @param indirectOffset - Byte offset in buffer

241

*/

242

dispatchWorkgroupsIndirect(

243

indirectBuffer: GPUBuffer,

244

indirectOffset: GPUSize64

245

): void;

246

247

/** Ends the compute pass */

248

end(): void;

249

250

// Inherited from GPUBindingCommandsMixin

251

setBindGroup(index: GPUIndex32, bindGroup: GPUBindGroup | null, dynamicOffsets?: Iterable<GPUBufferDynamicOffset>): void;

252

253

// Inherited from GPUDebugCommandsMixin

254

pushDebugGroup(groupLabel: string): void;

255

popDebugGroup(): void;

256

insertDebugMarker(markerLabel: string): void;

257

}

258

```

259

260

### Render Pass Configuration

261

262

Configuration for render passes including attachments and timing.

263

264

```typescript { .api }

265

interface GPURenderPassDescriptor extends GPUObjectDescriptorBase {

266

/** Color attachments */

267

colorAttachments: Iterable<GPURenderPassColorAttachment | null>;

268

269

/** Depth/stencil attachment */

270

depthStencilAttachment?: GPURenderPassDepthStencilAttachment;

271

272

/** Occlusion query set */

273

occlusionQuerySet?: GPUQuerySet;

274

275

/** Timestamp query writes */

276

timestampWrites?: GPURenderPassTimestampWrites;

277

278

/** Maximum number of draw commands */

279

maxDrawCount?: GPUSize64;

280

}

281

282

interface GPURenderPassColorAttachment {

283

/** Color attachment texture view */

284

view: GPUTextureView;

285

286

/** Depth slice for 3D textures */

287

depthSlice?: GPUIntegerCoordinate;

288

289

/** Resolve target for multisampled textures */

290

resolveTarget?: GPUTextureView;

291

292

/** Clear value when loadOp is "clear" */

293

clearValue?: GPUColor;

294

295

/** Load operation */

296

loadOp: GPULoadOp;

297

298

/** Store operation */

299

storeOp: GPUStoreOp;

300

}

301

302

interface GPURenderPassDepthStencilAttachment {

303

/** Depth/stencil texture view */

304

view: GPUTextureView;

305

306

/** Depth clear value */

307

depthClearValue?: number;

308

309

/** Depth load operation */

310

depthLoadOp?: GPULoadOp;

311

312

/** Depth store operation */

313

depthStoreOp?: GPUStoreOp;

314

315

/** Whether depth is read-only */

316

depthReadOnly?: boolean;

317

318

/** Stencil clear value */

319

stencilClearValue?: GPUStencilValue;

320

321

/** Stencil load operation */

322

stencilLoadOp?: GPULoadOp;

323

324

/** Stencil store operation */

325

stencilStoreOp?: GPUStoreOp;

326

327

/** Whether stencil is read-only */

328

stencilReadOnly?: boolean;

329

}

330

331

interface GPURenderPassTimestampWrites {

332

/** Query set for timestamps */

333

querySet: GPUQuerySet;

334

335

/** Query index for beginning of pass */

336

beginningOfPassWriteIndex?: GPUSize32;

337

338

/** Query index for end of pass */

339

endOfPassWriteIndex?: GPUSize32;

340

}

341

```

342

343

### Compute Pass Configuration

344

345

Configuration for compute passes.

346

347

```typescript { .api }

348

interface GPUComputePassDescriptor extends GPUObjectDescriptorBase {

349

/** Timestamp query writes */

350

timestampWrites?: GPUComputePassTimestampWrites;

351

}

352

353

interface GPUComputePassTimestampWrites {

354

/** Query set for timestamps */

355

querySet: GPUQuerySet;

356

357

/** Query index for beginning of pass */

358

beginningOfPassWriteIndex?: GPUSize32;

359

360

/** Query index for end of pass */

361

endOfPassWriteIndex?: GPUSize32;

362

}

363

```

364

365

### Command Mixin Interfaces

366

367

Shared command functionality across different encoder types.

368

369

```typescript { .api }

370

interface GPUBindingCommandsMixin {

371

/**

372

* Binds a bind group to a pipeline

373

* @param index - Bind group index

374

* @param bindGroup - Bind group to bind (null to unbind)

375

* @param dynamicOffsets - Dynamic buffer offsets

376

*/

377

setBindGroup(

378

index: GPUIndex32,

379

bindGroup: GPUBindGroup | null,

380

dynamicOffsets?: Iterable<GPUBufferDynamicOffset>

381

): void;

382

}

383

384

interface GPUDebugCommandsMixin {

385

/** Pushes a debug group for annotation */

386

pushDebugGroup(groupLabel: string): void;

387

388

/** Pops the current debug group */

389

popDebugGroup(): void;

390

391

/** Inserts a debug marker */

392

insertDebugMarker(markerLabel: string): void;

393

}

394

395

interface GPURenderCommandsMixin extends GPUBindingCommandsMixin, GPUDebugCommandsMixin {

396

/**

397

* Sets the render pipeline

398

* @param pipeline - Render pipeline to use

399

*/

400

setPipeline(pipeline: GPURenderPipeline): void;

401

402

/**

403

* Sets the index buffer

404

* @param buffer - Index buffer

405

* @param format - Index format (uint16 or uint32)

406

* @param offset - Byte offset in buffer

407

* @param size - Number of bytes to bind

408

*/

409

setIndexBuffer(

410

buffer: GPUBuffer,

411

format: GPUIndexFormat,

412

offset?: GPUSize64,

413

size?: GPUSize64

414

): void;

415

416

/**

417

* Sets a vertex buffer

418

* @param slot - Vertex buffer slot

419

* @param buffer - Vertex buffer (null to unbind)

420

* @param offset - Byte offset in buffer

421

* @param size - Number of bytes to bind

422

*/

423

setVertexBuffer(

424

slot: GPUIndex32,

425

buffer: GPUBuffer | null,

426

offset?: GPUSize64,

427

size?: GPUSize64

428

): void;

429

430

/**

431

* Draws primitives

432

* @param vertexCount - Number of vertices to draw

433

* @param instanceCount - Number of instances (default: 1)

434

* @param firstVertex - First vertex index (default: 0)

435

* @param firstInstance - First instance index (default: 0)

436

*/

437

draw(

438

vertexCount: GPUSize32,

439

instanceCount?: GPUSize32,

440

firstVertex?: GPUSize32,

441

firstInstance?: GPUSize32

442

): void;

443

444

/**

445

* Draws indexed primitives

446

* @param indexCount - Number of indices to draw

447

* @param instanceCount - Number of instances (default: 1)

448

* @param firstIndex - First index (default: 0)

449

* @param baseVertex - Value added to vertex index (default: 0)

450

* @param firstInstance - First instance index (default: 0)

451

*/

452

drawIndexed(

453

indexCount: GPUSize32,

454

instanceCount?: GPUSize32,

455

firstIndex?: GPUSize32,

456

baseVertex?: GPUSignedOffset32,

457

firstInstance?: GPUSize32

458

): void;

459

460

/**

461

* Draws with parameters from buffer

462

* @param indirectBuffer - Buffer containing draw parameters

463

* @param indirectOffset - Byte offset in buffer

464

*/

465

drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: GPUSize64): void;

466

467

/**

468

* Draws indexed with parameters from buffer

469

* @param indirectBuffer - Buffer containing draw parameters

470

* @param indirectOffset - Byte offset in buffer

471

*/

472

drawIndexedIndirect(indirectBuffer: GPUBuffer, indirectOffset: GPUSize64): void;

473

}

474

```

475

476

### Copy Operations Data Structures

477

478

Data structures for texture and buffer copy operations.

479

480

```typescript { .api }

481

interface GPUTexelCopyBufferInfo {

482

/** Buffer to copy from/to */

483

buffer: GPUBuffer;

484

485

/** Byte offset in buffer */

486

offset?: GPUSize64;

487

488

/** Bytes per row of texel data */

489

bytesPerRow?: GPUSize32;

490

491

/** Rows per image */

492

rowsPerImage?: GPUSize32;

493

}

494

495

interface GPUTexelCopyTextureInfo {

496

/** Texture to copy from/to */

497

texture: GPUTexture;

498

499

/** Mip level to access */

500

mipLevel?: GPUIntegerCoordinate;

501

502

/** Origin for the copy */

503

origin?: GPUOrigin3D;

504

505

/** Texture aspect to access */

506

aspect?: GPUTextureAspect;

507

}

508

509

interface GPUCopyExternalImageSourceInfo {

510

/** External image source */

511

source: GPUCopyExternalImageSource;

512

513

/** Origin in the source */

514

origin?: GPUOrigin2D;

515

516

/** Whether to flip Y coordinate */

517

flipY?: boolean;

518

}

519

520

interface GPUCopyExternalImageDestInfo {

521

/** Destination texture */

522

texture: GPUTexture;

523

524

/** Mip level to write to */

525

mipLevel?: GPUIntegerCoordinate;

526

527

/** Origin in destination */

528

origin?: GPUOrigin3D;

529

530

/** Texture aspect to write to */

531

aspect?: GPUTextureAspect;

532

533

/** Color space conversion */

534

colorSpace?: GPUImageCopyColorSpace;

535

536

/** Premultiply alpha */

537

premultipliedAlpha?: boolean;

538

}

539

540

type GPUImageCopyColorSpace = "srgb" | "display-p3";

541

```

542

543

## Usage Examples

544

545

### Basic Rendering Commands

546

547

```typescript

548

// Create command encoder

549

const encoder = device.createCommandEncoder({ label: "Main Render Commands" });

550

551

// Begin render pass

552

const renderPass = encoder.beginRenderPass({

553

colorAttachments: [{

554

view: context.getCurrentTexture().createView(),

555

clearValue: { r: 0.2, g: 0.3, b: 0.8, a: 1.0 },

556

loadOp: "clear",

557

storeOp: "store"

558

}],

559

depthStencilAttachment: {

560

view: depthTexture.createView(),

561

depthClearValue: 1.0,

562

depthLoadOp: "clear",

563

depthStoreOp: "store"

564

},

565

label: "Main Render Pass"

566

});

567

568

// Set pipeline and resources

569

renderPass.setPipeline(renderPipeline);

570

renderPass.setBindGroup(0, uniformBindGroup);

571

renderPass.setBindGroup(1, materialBindGroup);

572

renderPass.setVertexBuffer(0, vertexBuffer);

573

renderPass.setIndexBuffer(indexBuffer, "uint16");

574

575

// Draw geometry

576

renderPass.drawIndexed(indexCount);

577

578

// End pass and submit

579

renderPass.end();

580

const commandBuffer = encoder.finish();

581

device.queue.submit([commandBuffer]);

582

```

583

584

### Compute Operations

585

586

```typescript

587

// Create compute command encoder

588

const computeEncoder = device.createCommandEncoder({ label: "Compute Commands" });

589

590

// Begin compute pass

591

const computePass = computeEncoder.beginComputePass({

592

label: "Data Processing Pass"

593

});

594

595

// Set compute pipeline and bind groups

596

computePass.setPipeline(computePipeline);

597

computePass.setBindGroup(0, computeBindGroup);

598

599

// Dispatch compute workgroups

600

const workgroupsX = Math.ceil(dataSize / 64); // 64 = workgroup size

601

computePass.dispatchWorkgroups(workgroupsX);

602

603

// End pass

604

computePass.end();

605

606

// Submit compute work

607

const computeCommandBuffer = computeEncoder.finish();

608

device.queue.submit([computeCommandBuffer]);

609

```

610

611

### Buffer and Texture Copies

612

613

```typescript

614

const copyEncoder = device.createCommandEncoder({ label: "Copy Commands" });

615

616

// Copy buffer to buffer

617

copyEncoder.copyBufferToBuffer(

618

sourceBuffer, 0, // source and offset

619

destBuffer, 256, // destination and offset

620

1024 // size in bytes

621

);

622

623

// Copy buffer to texture

624

copyEncoder.copyBufferToTexture(

625

{

626

buffer: imageBuffer,

627

bytesPerRow: 1024 * 4, // 4 bytes per pixel

628

rowsPerImage: 768

629

},

630

{

631

texture: colorTexture,

632

mipLevel: 0

633

},

634

{ width: 1024, height: 768, depthOrArrayLayers: 1 }

635

);

636

637

// Copy texture to texture (mip generation)

638

for (let mip = 1; mip < mipLevels; mip++) {

639

const mipSize = Math.max(1, textureSize >> mip);

640

641

copyEncoder.copyTextureToTexture(

642

{

643

texture: sourceTexture,

644

mipLevel: mip - 1

645

},

646

{

647

texture: sourceTexture,

648

mipLevel: mip

649

},

650

{ width: mipSize, height: mipSize, depthOrArrayLayers: 1 }

651

);

652

}

653

654

device.queue.submit([copyEncoder.finish()]);

655

```

656

657

### Multi-Pass Rendering

658

659

```typescript

660

const frameEncoder = device.createCommandEncoder({ label: "Frame Commands" });

661

662

// Shadow pass

663

const shadowPass = frameEncoder.beginRenderPass({

664

colorAttachments: [],

665

depthStencilAttachment: {

666

view: shadowMap.createView(),

667

depthClearValue: 1.0,

668

depthLoadOp: "clear",

669

depthStoreOp: "store"

670

},

671

label: "Shadow Pass"

672

});

673

674

shadowPass.setPipeline(shadowPipeline);

675

shadowPass.setBindGroup(0, lightBindGroup);

676

// ... render shadow casters

677

shadowPass.end();

678

679

// Main render pass

680

const mainPass = frameEncoder.beginRenderPass({

681

colorAttachments: [{

682

view: colorTarget.createView(),

683

clearValue: { r: 0.1, g: 0.1, b: 0.2, a: 1.0 },

684

loadOp: "clear",

685

storeOp: "store"

686

}],

687

depthStencilAttachment: {

688

view: depthBuffer.createView(),

689

depthClearValue: 1.0,

690

depthLoadOp: "clear",

691

depthStoreOp: "store"

692

},

693

label: "Main Pass"

694

});

695

696

mainPass.setPipeline(mainPipeline);

697

mainPass.setBindGroup(0, sceneBindGroup);

698

mainPass.setBindGroup(1, shadowBindGroup); // Shadow map from previous pass

699

// ... render scene

700

mainPass.end();

701

702

// Post-processing pass

703

const postPass = frameEncoder.beginRenderPass({

704

colorAttachments: [{

705

view: context.getCurrentTexture().createView(),

706

loadOp: "load",

707

storeOp: "store"

708

}],

709

label: "Post Process Pass"

710

});

711

712

postPass.setPipeline(postProcessPipeline);

713

postPass.setBindGroup(0, postProcessBindGroup);

714

postPass.draw(3); // Fullscreen triangle

715

postPass.end();

716

717

device.queue.submit([frameEncoder.finish()]);

718

```

719

720

### Query and Timing

721

722

```typescript

723

// Create timestamp queries

724

const timestampQuerySet = device.createQuerySet({

725

type: "timestamp",

726

count: 4

727

});

728

729

const queryBuffer = device.createBuffer({

730

size: 8 * 4, // 8 bytes per timestamp

731

usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC

732

});

733

734

const readBuffer = device.createBuffer({

735

size: 8 * 4,

736

usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ

737

});

738

739

// Record with timing

740

const timedEncoder = device.createCommandEncoder();

741

742

timedEncoder.writeTimestamp(timestampQuerySet, 0); // Frame start

743

744

const renderPass = timedEncoder.beginRenderPass({

745

colorAttachments: [/* ... */],

746

timestampWrites: {

747

querySet: timestampQuerySet,

748

beginningOfPassWriteIndex: 1,

749

endOfPassWriteIndex: 2

750

}

751

});

752

753

// ... rendering commands ...

754

renderPass.end();

755

756

timedEncoder.writeTimestamp(timestampQuerySet, 3); // Frame end

757

758

// Resolve timestamps

759

timedEncoder.resolveQuerySet(timestampQuerySet, 0, 4, queryBuffer, 0);

760

timedEncoder.copyBufferToBuffer(queryBuffer, 0, readBuffer, 0, 8 * 4);

761

762

device.queue.submit([timedEncoder.finish()]);

763

764

// Read timing results

765

await readBuffer.mapAsync(GPUMapMode.READ);

766

const times = new BigUint64Array(readBuffer.getMappedRange());

767

const frameTime = Number(times[3] - times[0]) / 1000000; // Convert to milliseconds

768

console.log(`Frame time: ${frameTime.toFixed(2)}ms`);

769

readBuffer.unmap();

770

```

771

772

### Debug Annotations

773

774

```typescript

775

const debugEncoder = device.createCommandEncoder({ label: "Debug Annotated Commands" });

776

777

debugEncoder.pushDebugGroup("Scene Rendering");

778

779

const renderPass = debugEncoder.beginRenderPass({

780

colorAttachments: [/* ... */],

781

label: "Scene Pass"

782

});

783

784

renderPass.pushDebugGroup("Opaque Objects");

785

renderPass.setPipeline(opaquePipeline);

786

// ... render opaque objects

787

renderPass.popDebugGroup();

788

789

renderPass.insertDebugMarker("Begin Transparent Objects");

790

renderPass.setPipeline(transparentPipeline);

791

// ... render transparent objects

792

793

renderPass.end();

794

debugEncoder.popDebugGroup();

795

796

debugEncoder.pushDebugGroup("Post Processing");

797

// ... post processing passes

798

debugEncoder.popDebugGroup();

799

800

device.queue.submit([debugEncoder.finish()]);

801

```