Capture: WriteTimestamp

Bug: 464001949
Change-Id: I6a6a6964ba3d9b75ddd39a4e89c823f3be18d7cd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/278537
Reviewed-by: Shrek Shao <shrekshao@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Gregg Tavares <gman@chromium.org>
diff --git a/src/dawn/native/webgpu/CommandBufferWGPU.cpp b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
index 80722f0..c30a57d 100644
--- a/src/dawn/native/webgpu/CommandBufferWGPU.cpp
+++ b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
@@ -485,6 +485,17 @@
     return {};
 }
 
+void CaptureTimestampWriteCommand(CaptureContext& captureContext, CommandIterator& commands) {
+    const auto& cmd = *commands.NextCommand<WriteTimestampCmd>();
+    schema::CommandBufferCommandWriteTimestampCmd data{{
+        .data = {{
+            .querySetId = captureContext.GetId(cmd.querySet),
+            .queryIndex = cmd.queryIndex,
+        }},
+    }};
+    Serialize(captureContext, data);
+}
+
 MaybeError CaptureComputePass(CaptureContext& captureContext, CommandIterator& commands) {
     Command type;
     while (commands.NextCommandId(&type)) {
@@ -527,6 +538,9 @@
                 Serialize(captureContext, data);
                 break;
             }
+            case Command::WriteTimestamp:
+                CaptureTimestampWriteCommand(captureContext, commands);
+                break;
             case Command::SetBindGroup:
             case Command::SetImmediates:
                 CaptureSharedCommand(captureContext, commands, type);
@@ -630,6 +644,9 @@
                 Serialize(captureContext, data);
                 break;
             }
+            case Command::WriteTimestamp:
+                CaptureTimestampWriteCommand(captureContext, commands);
+                break;
             default:
                 DAWN_TRY(CaptureRenderCommand(captureContext, commands, type));
                 break;
@@ -662,6 +679,9 @@
     for (auto texture : resourceUsages.topLevelTextures) {
         DAWN_TRY(captureContext.AddResource(ToBackend(texture)));
     }
+    for (auto querySet : resourceUsages.usedQuerySets) {
+        DAWN_TRY(captureContext.AddResource(ToBackend(querySet)));
+    }
     DAWN_TRY(AddReferencedPassResourceUsages(captureContext, resourceUsages.renderPasses));
     for (const auto& pass : resourceUsages.computePasses) {
         DAWN_TRY(AddReferencedPassResourceUsages(captureContext, pass.dispatchUsages));
@@ -714,6 +734,7 @@
             case Command::CopyBufferToTexture:
             case Command::CopyTextureToBuffer:
             case Command::CopyTextureToTexture:
+            case Command::WriteTimestamp:
             case Command::PushDebugGroup:
             case Command::InsertDebugMarker:
             case Command::PopDebugGroup:
@@ -899,6 +920,9 @@
                 Serialize(captureContext, data);
                 break;
             }
+            case Command::WriteTimestamp:
+                CaptureTimestampWriteCommand(captureContext, commands);
+                break;
             case Command::PushDebugGroup:
             case Command::PopDebugGroup:
             case Command::InsertDebugMarker:
diff --git a/src/dawn/replay/Replay.cpp b/src/dawn/replay/Replay.cpp
index 2dbda14..da07035 100644
--- a/src/dawn/replay/Replay.cpp
+++ b/src/dawn/replay/Replay.cpp
@@ -425,6 +425,14 @@
 }
 
 template <typename T>
+MaybeError ProcessWriteTimestamp(const Replay& replay, T pass, ReadHead& readHead) {
+    schema::CommandBufferCommandWriteTimestampCmdData data;
+    DAWN_TRY(Deserialize(readHead, &data));
+    pass.WriteTimestamp(replay.GetObjectById<wgpu::QuerySet>(data.querySetId), data.queryIndex);
+    return {};
+}
+
+template <typename T>
 MaybeError ProcessSharedCommands(const Replay& replay,
                                  T pass,
                                  schema::CommandBufferCommand cmd,
@@ -811,6 +819,9 @@
                                                 data.offset);
                 break;
             }
+            case schema::CommandBufferCommand::WriteTimestamp:
+                DAWN_TRY(ProcessWriteTimestamp(replay, pass, readHead));
+                break;
             case schema::CommandBufferCommand::SetBindGroup:
             case schema::CommandBufferCommand::SetImmediates:
                 DAWN_TRY(ProcessSharedCommands(replay, pass, cmd, readHead));
@@ -886,6 +897,9 @@
                                  data.maxDepth);
                 break;
             }
+            case schema::CommandBufferCommand::WriteTimestamp:
+                DAWN_TRY(ProcessWriteTimestamp(replay, pass, readHead));
+                break;
             default:
                 DAWN_TRY(ProcessRenderCommand(replay, readHead, device, cmd, pass));
                 break;
@@ -1016,6 +1030,9 @@
                                         data.destinationOffset);
                 break;
             }
+            case schema::CommandBufferCommand::WriteTimestamp:
+                DAWN_TRY(ProcessWriteTimestamp(replay, encoder, readHead));
+                break;
             case schema::CommandBufferCommand::PushDebugGroup:
             case schema::CommandBufferCommand::InsertDebugMarker:
             case schema::CommandBufferCommand::PopDebugGroup:
diff --git a/src/dawn/serialization/Schema.h b/src/dawn/serialization/Schema.h
index 72d13e1..8b4f314 100644
--- a/src/dawn/serialization/Schema.h
+++ b/src/dawn/serialization/Schema.h
@@ -688,6 +688,12 @@
 DAWN_REPLAY_MAKE_COMMAND_BUFFER_CMD_AND_CMD_DATA(DrawIndexedIndirect,
                                                  DRAW_INDIRECT_CMD_DATA_MEMBER){};
 
+#define WRITE_TIMESTAMP_CMD_DATA_MEMBER(X) \
+    X(ObjectId, querySetId)                \
+    X(uint32_t, queryIndex)
+
+DAWN_REPLAY_MAKE_COMMAND_BUFFER_CMD_AND_CMD_DATA(WriteTimestamp, WRITE_TIMESTAMP_CMD_DATA_MEMBER){};
+
 }  // namespace schema
 
 #endif  // SRC_DAWN_SERIALIZATION_SCHEMA_H_
diff --git a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
index cdd3cdc..d1c8b8c 100644
--- a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
+++ b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
@@ -2366,5 +2366,66 @@
 
 DAWN_INSTANTIATE_TEST(CaptureAndReplayDrawTests, WebGPUBackend());
 
+class CaptureAndReplayTimestampTests : public CaptureAndReplayTests {
+  protected:
+    void SetUp() override {
+        CaptureAndReplayTests::SetUp();
+        DAWN_TEST_UNSUPPORTED_IF(
+            !SupportsFeatures({wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses}));
+    }
+
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        std::vector<wgpu::FeatureName> requiredFeatures = {};
+        if (SupportsFeatures({wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses})) {
+            requiredFeatures.push_back(
+                wgpu::FeatureName::ChromiumExperimentalTimestampQueryInsidePasses);
+            requiredFeatures.push_back(wgpu::FeatureName::TimestampQuery);
+        }
+        return requiredFeatures;
+    }
+};
+
+// Test WriteTimestamp in compute pass, render pass, and
+// command buffer. We don't expect any results. We only
+// expect it doesn't get any errors.
+TEST_P(CaptureAndReplayTimestampTests, WriteTimestamp) {
+    wgpu::QuerySetDescriptor qsDesc;
+    qsDesc.label = "myQuerySet";
+    qsDesc.count = 3;
+    qsDesc.type = wgpu::QueryType::Timestamp;
+    wgpu::QuerySet querySet = device.CreateQuerySet(&qsDesc);
+
+    wgpu::CommandBuffer commands;
+    {
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        encoder.WriteTimestamp(querySet, 0);
+        {
+            utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
+            wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+            pass.WriteTimestamp(querySet, 1);
+            pass.End();
+        }
+        {
+            wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+            pass.WriteTimestamp(querySet, 2);
+            pass.End();
+        }
+        commands = encoder.Finish();
+    }
+
+    // --- capture ---
+    auto recorder = Recorder::CreateAndStart(device);
+
+    queue.Submit(1, &commands);
+
+    // --- replay ---
+    auto capture = recorder.Finish();
+    auto replay = capture.Replay(device);
+
+    // just expect no errors.
+}
+
+DAWN_INSTANTIATE_TEST(CaptureAndReplayTimestampTests, WebGPUBackend());
+
 }  // anonymous namespace
 }  // namespace dawn