Capture: Handle Debug Markers.

Bug: 451479048
Change-Id: I6a6a6964dd79fdc53d7dc6bbba8f3f0ba30e9e50
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/275395
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Shrek Shao <shrekshao@google.com>
Commit-Queue: Gregg Tavares <gman@chromium.org>
diff --git a/src/dawn/native/webgpu/CommandBufferHelpers.cpp b/src/dawn/native/webgpu/CommandBufferHelpers.cpp
index ad2bbd2..35403b1 100644
--- a/src/dawn/native/webgpu/CommandBufferHelpers.cpp
+++ b/src/dawn/native/webgpu/CommandBufferHelpers.cpp
@@ -40,6 +40,43 @@
 
 namespace dawn::native::webgpu {
 
+// Returns true if command was handled
+bool CaptureDebugCommand(CaptureContext& captureContext, CommandIterator& commands, Command type) {
+    switch (type) {
+        case Command::PushDebugGroup: {
+            const auto& cmd = *commands.NextCommand<PushDebugGroupCmd>();
+            const char* label = commands.NextData<char>(cmd.length + 1);
+            schema::CommandBufferCommandPushDebugGroupCmd data{{
+                .data = {{
+                    .groupLabel = label,
+                }},
+            }};
+            Serialize(captureContext, data);
+            break;
+        }
+        case Command::PopDebugGroup: {
+            commands.NextCommand<PopDebugGroupCmd>();
+            Serialize(captureContext, schema::CommandBufferCommand::PopDebugGroup);
+            break;
+        }
+        case Command::InsertDebugMarker: {
+            const auto& cmd = *commands.NextCommand<InsertDebugMarkerCmd>();
+            const char* label = commands.NextData<char>(cmd.length + 1);
+            schema::CommandBufferCommandInsertDebugMarkerCmd data{{
+                .data = {{
+                    .markerLabel = label,
+                }},
+            }};
+            Serialize(captureContext, data);
+            break;
+        }
+        default: {
+            return false;
+        }
+    }
+    return true;
+}
+
 // Captures commands common to a render pass and a render bundle
 MaybeError CaptureRenderCommand(CaptureContext& captureContext,
                                 CommandIterator& commands,
@@ -97,21 +134,17 @@
             Serialize(captureContext, data);
             break;
         }
-        default:
-            return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command");
+        default: {
+            if (!CaptureDebugCommand(captureContext, commands, type)) {
+                return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command");
+            }
+            break;
+        }
     }
 
     return {};
 }
 
-// Commands are encoded with a command id followed by command-specific data.
-// so we're required to read each command to skip over them.
-#define DAWN_SKIP_COMMAND(cmdName)            \
-    case Command::cmdName: {                  \
-        commands.NextCommand<cmdName##Cmd>(); \
-        break;                                \
-    }
-
 // Gathers resources used by commands from both render passes and render bundles.
 MaybeError GatherReferencedResourcesFromRenderCommand(CaptureContext& captureContext,
                                                       CommandIterator& commands,
@@ -128,22 +161,23 @@
             usedResources.bindGroups.push_back(cmd->group.Get());
             break;
         }
-            DAWN_SKIP_COMMAND(BeginOcclusionQuery)
-            DAWN_SKIP_COMMAND(EndOcclusionQuery)
-            DAWN_SKIP_COMMAND(Draw)
-            DAWN_SKIP_COMMAND(DrawIndexed)
-            DAWN_SKIP_COMMAND(DrawIndirect)
-            DAWN_SKIP_COMMAND(DrawIndexedIndirect)
-            DAWN_SKIP_COMMAND(InsertDebugMarker)
-            DAWN_SKIP_COMMAND(PopDebugGroup)
-            DAWN_SKIP_COMMAND(PushDebugGroup)
-            DAWN_SKIP_COMMAND(WriteTimestamp)
-            DAWN_SKIP_COMMAND(SetImmediates)
-            DAWN_SKIP_COMMAND(SetIndexBuffer)
-            DAWN_SKIP_COMMAND(SetVertexBuffer)
-        default: {
+        case Command::BeginOcclusionQuery:
+        case Command::EndOcclusionQuery:
+        case Command::Draw:
+        case Command::DrawIndexed:
+        case Command::DrawIndirect:
+        case Command::DrawIndexedIndirect:
+        case Command::WriteTimestamp:
+        case Command::SetImmediates:
+        case Command::SetIndexBuffer:
+        case Command::SetVertexBuffer:
+        case Command::PushDebugGroup:
+        case Command::InsertDebugMarker:
+        case Command::PopDebugGroup:
+            SkipCommand(&commands, type);
+            break;
+        default:
             return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command");
-        }
     }
     return {};
 }
diff --git a/src/dawn/native/webgpu/CommandBufferHelpers.h b/src/dawn/native/webgpu/CommandBufferHelpers.h
index 691b018..88f9d19 100644
--- a/src/dawn/native/webgpu/CommandBufferHelpers.h
+++ b/src/dawn/native/webgpu/CommandBufferHelpers.h
@@ -57,6 +57,9 @@
     std::vector<RenderBundleBase*> renderBundles;
 };
 
+// Returns true if command was handled.
+bool CaptureDebugCommand(CaptureContext& captureContext, CommandIterator& commands, Command type);
+
 // Captures shared commands from both render passes and render bundles
 MaybeError CaptureRenderCommand(CaptureContext& captureContext,
                                 CommandIterator& commands,
diff --git a/src/dawn/native/webgpu/CommandBufferWGPU.cpp b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
index b3c44b5..f1bf8e4 100644
--- a/src/dawn/native/webgpu/CommandBufferWGPU.cpp
+++ b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
@@ -411,14 +411,6 @@
     DAWN_UNREACHABLE();
 }
 
-// Commands are encoded with a command id followed by command-specific data.
-// so we're required to read each command to skip over them.
-#define DAWN_SKIP_COMMAND(cmdName)            \
-    case Command::cmdName: {                  \
-        commands.NextCommand<cmdName##Cmd>(); \
-        break;                                \
-    }
-
 MaybeError GatherReferencedResourcesFromComputePass(CaptureContext& captureContext,
                                                     CommandIterator& commands,
                                                     CommandBufferResourceUsages& usedResources) {
@@ -439,16 +431,17 @@
                 usedResources.bindGroups.push_back(cmd->group.Get());
                 break;
             }
-                DAWN_SKIP_COMMAND(Dispatch)
-                DAWN_SKIP_COMMAND(DispatchIndirect)
-                DAWN_SKIP_COMMAND(InsertDebugMarker)
-                DAWN_SKIP_COMMAND(PopDebugGroup)
-                DAWN_SKIP_COMMAND(PushDebugGroup)
-                DAWN_SKIP_COMMAND(WriteTimestamp)
-                DAWN_SKIP_COMMAND(SetImmediates)
-            default: {
-                DAWN_CHECK(false);
+            case Command::Dispatch:
+            case Command::DispatchIndirect:
+            case Command::WriteTimestamp:
+            case Command::SetImmediates:
+            case Command::PushDebugGroup:
+            case Command::InsertDebugMarker:
+            case Command::PopDebugGroup:
+                SkipCommand(&commands, type);
                 break;
+            default: {
+                return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command");
             }
         }
     }
@@ -535,8 +528,12 @@
                 Serialize(captureContext, data);
                 break;
             }
-            default:
-                DAWN_CHECK(false);
+            default: {
+                if (!CaptureDebugCommand(captureContext, commands, type)) {
+                    return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command");
+                }
+                break;
+            }
         }
     }
     return {};
@@ -685,12 +682,18 @@
                                                                  usedResources));
                 break;
             }
-                DAWN_SKIP_COMMAND(CopyBufferToBuffer)
-                DAWN_SKIP_COMMAND(CopyBufferToTexture)
-                DAWN_SKIP_COMMAND(CopyTextureToBuffer)
-                DAWN_SKIP_COMMAND(CopyTextureToTexture)
-            default:
-                DAWN_CHECK(false);
+            case Command::CopyBufferToBuffer:
+            case Command::CopyBufferToTexture:
+            case Command::CopyTextureToBuffer:
+            case Command::CopyTextureToTexture:
+            case Command::PushDebugGroup:
+            case Command::InsertDebugMarker:
+            case Command::PopDebugGroup:
+                SkipCommand(&commands, type);
+                break;
+            default: {
+                return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command");
+            }
         }
     }
 
@@ -854,8 +857,12 @@
                 Serialize(captureContext, data);
                 break;
             }
-            default:
-                DAWN_CHECK(false);
+            default: {
+                if (!CaptureDebugCommand(captureContext, commands, type)) {
+                    return DAWN_UNIMPLEMENTED_ERROR("Unimplemented command");
+                }
+                break;
+            }
         }
     }
     Serialize(captureContext, schema::CommandBufferCommand::End);
diff --git a/src/dawn/replay/Replay.cpp b/src/dawn/replay/Replay.cpp
index d81445c..92e4532 100644
--- a/src/dawn/replay/Replay.cpp
+++ b/src/dawn/replay/Replay.cpp
@@ -423,6 +423,32 @@
 }
 
 template <typename T>
+MaybeError ProcessDebugCommands(T pass, schema::CommandBufferCommand cmd, ReadHead& readHead) {
+    switch (cmd) {
+        case schema::CommandBufferCommand::PushDebugGroup: {
+            schema::CommandBufferCommandPushDebugGroupCmdData data;
+            DAWN_TRY(Deserialize(readHead, &data));
+            pass.PushDebugGroup(wgpu::StringView(data.groupLabel));
+            break;
+        }
+        case schema::CommandBufferCommand::InsertDebugMarker: {
+            schema::CommandBufferCommandInsertDebugMarkerCmdData data;
+            DAWN_TRY(Deserialize(readHead, &data));
+            pass.InsertDebugMarker(wgpu::StringView(data.markerLabel));
+            break;
+        }
+        case schema::CommandBufferCommand::PopDebugGroup: {
+            // PopDebugGroup has no data
+            pass.PopDebugGroup();
+            break;
+        }
+        default:
+            DAWN_UNREACHABLE();
+    }
+    return {};
+}
+
+template <typename T>
 MaybeError ProcessRenderCommand(const Replay& replay,
                                 ReadHead& readHead,
                                 wgpu::Device device,
@@ -455,6 +481,11 @@
             pass.Draw(data.vertexCount, data.instanceCount, data.firstVertex, data.firstInstance);
             break;
         }
+        case schema::CommandBufferCommand::PushDebugGroup:
+        case schema::CommandBufferCommand::InsertDebugMarker:
+        case schema::CommandBufferCommand::PopDebugGroup:
+            DAWN_TRY(ProcessDebugCommands(pass, cmd, readHead));
+            break;
         default:
             return DAWN_INTERNAL_ERROR("Render Pass/Bundle Command not implemented");
     }
@@ -729,6 +760,11 @@
                 pass.DispatchWorkgroups(data.x, data.y, data.z);
                 break;
             }
+            case schema::CommandBufferCommand::PushDebugGroup:
+            case schema::CommandBufferCommand::InsertDebugMarker:
+            case schema::CommandBufferCommand::PopDebugGroup:
+                DAWN_TRY(ProcessDebugCommands(pass, cmd, readHead));
+                break;
             default:
                 return DAWN_INTERNAL_ERROR("Compute Pass Command not implemented");
         }
@@ -892,6 +928,11 @@
                                         data.destinationOffset);
                 break;
             }
+            case schema::CommandBufferCommand::PushDebugGroup:
+            case schema::CommandBufferCommand::InsertDebugMarker:
+            case schema::CommandBufferCommand::PopDebugGroup:
+                DAWN_TRY(ProcessDebugCommands(encoder, cmd, readHead));
+                break;
             default:
                 return DAWN_INTERNAL_ERROR("Encoder Command not implemented");
         }
diff --git a/src/dawn/serialization/Schema.h b/src/dawn/serialization/Schema.h
index e2fb69f..ae3375e 100644
--- a/src/dawn/serialization/Schema.h
+++ b/src/dawn/serialization/Schema.h
@@ -560,6 +560,16 @@
 DAWN_REPLAY_MAKE_COMMAND_BUFFER_CMD_AND_CMD_DATA(SetRenderPipeline,
                                                  SET_RENDER_PIPELINE_CMD_DATA_MEMBER){};
 
+#define PUSH_DEBUG_GROUP_CMD_DATA_MEMBER(X) X(std::string, groupLabel)
+
+DAWN_REPLAY_MAKE_COMMAND_BUFFER_CMD_AND_CMD_DATA(PushDebugGroup,
+                                                 PUSH_DEBUG_GROUP_CMD_DATA_MEMBER){};
+
+#define INSERT_DEBUG_MARKER_CMD_DATA_MEMBER(X) X(std::string, markerLabel)
+
+DAWN_REPLAY_MAKE_COMMAND_BUFFER_CMD_AND_CMD_DATA(InsertDebugMarker,
+                                                 INSERT_DEBUG_MARKER_CMD_DATA_MEMBER){};
+
 #define BEGIN_OCCLUSION_QUERY_CMD_DATA_MEMBER(X) X(uint32_t, queryIndex)
 
 DAWN_REPLAY_MAKE_COMMAND_BUFFER_CMD_AND_CMD_DATA(BeginOcclusionQuery,
diff --git a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
index 2155a57..879023e 100644
--- a/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
+++ b/src/dawn/tests/white_box/CaptureAndReplayTests.cpp
@@ -1714,6 +1714,43 @@
     ExpectTextureEQ(replay.get(), "dstTexture", {1}, expected);
 }
 
+// Test debug commands don't fail.
+TEST_P(CaptureAndReplayTests, PushPopInsertDebug) {
+    utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 4, 4);
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.PushDebugGroup("Event Start");
+    encoder.InsertDebugMarker("Marker");
+    encoder.PopDebugGroup();
+    {
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+        pass.PushDebugGroup("Event Start");
+        pass.InsertDebugMarker("Marker");
+        pass.PopDebugGroup();
+        pass.End();
+    }
+    {
+        wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+        pass.PushDebugGroup("Event Start");
+        pass.InsertDebugMarker("Marker");
+        pass.PopDebugGroup();
+        pass.End();
+    }
+
+    wgpu::CommandBuffer 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(CaptureAndReplayTests, WebGPUBackend());
 
 }  // anonymous namespace