Fix leak of Metal counter sample buffers

The blit descriptor allocation was leaking, which references the
counter sample buffer. Fix it by storing the descriptor in a
scoped NSRef

Fixed: dawn:1603
Change-Id: If40e8608db167717a4e07f3cb64a5e98402e3f1a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112861
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/metal/CommandBufferMTL.mm b/src/dawn/native/metal/CommandBufferMTL.mm
index 91aacbf..f1f5131 100644
--- a/src/dawn/native/metal/CommandBufferMTL.mm
+++ b/src/dawn/native/metal/CommandBufferMTL.mm
@@ -334,7 +334,8 @@
     API_AVAILABLE(macos(11.0), ios(14.0)) {
     commandContext->EndBlit();
 
-    MTLBlitPassDescriptor* descriptor = [[MTLBlitPassDescriptor alloc] init];
+    auto scopedDescriptor = AcquireNSRef([[MTLBlitPassDescriptor alloc] init]);
+    MTLBlitPassDescriptor* descriptor = scopedDescriptor.Get();
     if (cmd->querySet.Get() != nullptr) {
         descriptor.sampleBufferAttachments[0].sampleBuffer =
             ToBackend(cmd->querySet.Get())->GetCounterSampleBuffer();
diff --git a/src/dawn/tests/end2end/QueryTests.cpp b/src/dawn/tests/end2end/QueryTests.cpp
index f8cf53c..9d4ec5a 100644
--- a/src/dawn/tests/end2end/QueryTests.cpp
+++ b/src/dawn/tests/end2end/QueryTests.cpp
@@ -1078,6 +1078,26 @@
     EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
 }
 
+// Test calling WriteTimestamp many times into separate query sets.
+// Regression test for crbug.com/dawn/1603.
+TEST_P(TimestampQueryTests, ManyWriteTimestampDistinctQuerySets) {
+    constexpr uint32_t kQueryCount = 100;
+    // Write timestamp with a different query sets many times
+    for (uint32_t i = 0; i < kQueryCount; ++i) {
+        wgpu::QuerySet querySet = CreateQuerySetForTimestamp(1);
+
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        encoder.WriteTimestamp(querySet, 0);
+        wgpu::CommandBuffer commands = encoder.Finish();
+        queue.Submit(1, &commands);
+
+        // Destroy the query set so we don't OOM.
+        querySet.Destroy();
+        // Make sure the queue is idle so the query set is definitely destroyed.
+        WaitForAllOperations();
+    }
+}
+
 class TimestampQueryInsidePassesTests : public TimestampQueryTests {
   protected:
     void SetUp() override {