Update resolveQuerySet for unavailable queries on D3D12

Now resolving unavailable queries is undefined behaviour on D3D12, it
might cause device hangs or removal. Update to just resolve available
part like Vulkan did, and clear the destination buffer to 0s for the
unavailable part.

Bug: dawn:973

Change-Id: I7cf6808d67179425b4883e8847c76daaf8444dc2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/59420
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Commit-Queue: Hao Li <hao.x.li@intel.com>
diff --git a/src/dawn_native/d3d12/BufferD3D12.cpp b/src/dawn_native/d3d12/BufferD3D12.cpp
index 779bec0..b24a7a2 100644
--- a/src/dawn_native/d3d12/BufferD3D12.cpp
+++ b/src/dawn_native/d3d12/BufferD3D12.cpp
@@ -258,6 +258,13 @@
             return false;
         }
 
+        // TODO(crbug.com/dawn/1024): The before and after states must be different. Remove this
+        // workaround and use D3D12 states instead of WebGPU usages to manage the tracking of
+        // barrier state.
+        if (lastState == newState) {
+            return false;
+        }
+
         barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
         barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
         barrier->Transition.pResource = GetD3D12Resource();
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 38c9a93..7415911 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -16,6 +16,8 @@
 
 #include "dawn_native/BindGroupTracker.h"
 #include "dawn_native/CommandValidation.h"
+#include "dawn_native/DynamicUploader.h"
+#include "dawn_native/Error.h"
 #include "dawn_native/RenderBundle.h"
 #include "dawn_native/d3d12/BindGroupD3D12.h"
 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
@@ -27,6 +29,7 @@
 #include "dawn_native/d3d12/RenderPassBuilderD3D12.h"
 #include "dawn_native/d3d12/RenderPipelineD3D12.h"
 #include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
+#include "dawn_native/d3d12/StagingBufferD3D12.h"
 #include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h"
 #include "dawn_native/d3d12/UtilsD3D12.h"
 
@@ -93,6 +96,63 @@
                                   cmd->queryIndex);
         }
 
+        void RecordResolveQuerySetCmd(ID3D12GraphicsCommandList* commandList,
+                                      Device* device,
+                                      QuerySet* querySet,
+                                      uint32_t firstQuery,
+                                      uint32_t queryCount,
+                                      Buffer* destination,
+                                      uint64_t destinationOffset) {
+            const std::vector<bool>& availability = querySet->GetQueryAvailability();
+
+            auto currentIt = availability.begin() + firstQuery;
+            auto lastIt = availability.begin() + firstQuery + queryCount;
+
+            // Traverse available queries in the range of [firstQuery, firstQuery +  queryCount - 1]
+            while (currentIt != lastIt) {
+                auto firstTrueIt = std::find(currentIt, lastIt, true);
+                // No available query found for resolving
+                if (firstTrueIt == lastIt) {
+                    break;
+                }
+                auto nextFalseIt = std::find(firstTrueIt, lastIt, false);
+
+                // The query index of firstTrueIt where the resolving starts
+                uint32_t resolveQueryIndex = std::distance(availability.begin(), firstTrueIt);
+                // The queries count between firstTrueIt and nextFalseIt need to be resolved
+                uint32_t resolveQueryCount = std::distance(firstTrueIt, nextFalseIt);
+
+                // Calculate destinationOffset based on the current resolveQueryIndex and firstQuery
+                uint32_t resolveDestinationOffset =
+                    destinationOffset + (resolveQueryIndex - firstQuery) * sizeof(uint64_t);
+
+                // Resolve the queries between firstTrueIt and nextFalseIt (which is at most lastIt)
+                commandList->ResolveQueryData(
+                    querySet->GetQueryHeap(), D3D12QueryType(querySet->GetQueryType()),
+                    resolveQueryIndex, resolveQueryCount, destination->GetD3D12Resource(),
+                    resolveDestinationOffset);
+
+                // Set current iterator to next false
+                currentIt = nextFalseIt;
+            }
+        }
+
+        MaybeError ClearBufferToZero(Device* device,
+                                     Buffer* destination,
+                                     uint64_t destinationOffset,
+                                     uint64_t size) {
+            DynamicUploader* uploader = device->GetDynamicUploader();
+            UploadHandle uploadHandle;
+            DAWN_TRY_ASSIGN(uploadHandle,
+                            uploader->Allocate(size, device->GetPendingCommandSerial(),
+                                               kCopyBufferToBufferOffsetAlignment));
+            memset(uploadHandle.mappedBuffer, 0u, size);
+
+            return device->CopyFromStagingToBuffer(uploadHandle.stagingBuffer,
+                                                   uploadHandle.startOffset, destination,
+                                                   destinationOffset, size);
+        }
+
         void RecordFirstIndexOffset(ID3D12GraphicsCommandList* commandList,
                                     RenderPipeline* pipeline,
                                     uint32_t firstVertex,
@@ -840,18 +900,31 @@
                 case Command::ResolveQuerySet: {
                     ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
                     QuerySet* querySet = ToBackend(cmd->querySet.Get());
+                    uint32_t firstQuery = cmd->firstQuery;
+                    uint32_t queryCount = cmd->queryCount;
                     Buffer* destination = ToBackend(cmd->destination.Get());
+                    uint64_t destinationOffset = cmd->destinationOffset;
 
                     DAWN_TRY(destination->EnsureDataInitializedAsDestination(
-                        commandContext, cmd->destinationOffset,
-                        cmd->queryCount * sizeof(uint64_t)));
-                    destination->TrackUsageAndTransitionNow(commandContext,
-                                                            wgpu::BufferUsage::CopyDst);
+                        commandContext, destinationOffset, queryCount * sizeof(uint64_t)));
 
-                    commandList->ResolveQueryData(
-                        querySet->GetQueryHeap(), D3D12QueryType(querySet->GetQueryType()),
-                        cmd->firstQuery, cmd->queryCount, destination->GetD3D12Resource(),
-                        cmd->destinationOffset);
+                    // Resolving unavailable queries is undefined behaviour on D3D12, we only can
+                    // resolve the available part of sparse queries. In order to resolve the
+                    // unavailables as 0s, we need to clear the resolving region of the destination
+                    // buffer to 0s.
+                    auto startIt = querySet->GetQueryAvailability().begin() + firstQuery;
+                    auto endIt = querySet->GetQueryAvailability().begin() + firstQuery + queryCount;
+                    bool hasUnavailableQueries = std::find(startIt, endIt, false) != endIt;
+                    if (hasUnavailableQueries) {
+                        DAWN_TRY(ClearBufferToZero(device, destination, destinationOffset,
+                                                   queryCount * sizeof(uint64_t)));
+                    }
+
+                    destination->TrackUsageAndTransitionNow(commandContext,
+                                                            wgpu::BufferUsage::QueryResolve);
+
+                    RecordResolveQuerySetCmd(commandList, device, querySet, firstQuery, queryCount,
+                                             destination, destinationOffset);
 
                     break;
                 }