D3D12: Silence known debug layer warnings

This will help remove noise in debug layer output. This patch also
 promotes warnings and higher to Dawn errors.

BUG: dawn:363, dawn:418, dawn:419, dawn:421

Change-Id: I3112c94aff71fc7e76dff48c82bafe9e051ed3b4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21702
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/d3d12/AdapterD3D12.cpp b/src/dawn_native/d3d12/AdapterD3D12.cpp
index 4124a76..b2a35cb 100644
--- a/src/dawn_native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn_native/d3d12/AdapterD3D12.cpp
@@ -15,7 +15,9 @@
 #include "dawn_native/d3d12/AdapterD3D12.h"
 
 #include "common/Constants.h"
+#include "dawn_native/Instance.h"
 #include "dawn_native/d3d12/BackendD3D12.h"
+#include "dawn_native/d3d12/D3D12Error.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
 #include "dawn_native/d3d12/PlatformFunctions.h"
 
@@ -40,6 +42,10 @@
           mBackend(backend) {
     }
 
+    Adapter::~Adapter() {
+        CleanUpDebugLayerFilters();
+    }
+
     const D3D12DeviceInfo& Adapter::GetDeviceInfo() const {
         return mDeviceInfo;
     }
@@ -66,6 +72,8 @@
             return DAWN_INTERNAL_ERROR("D3D12CreateDevice failed");
         }
 
+        DAWN_TRY(InitializeDebugLayerFilters());
+
         DXGI_ADAPTER_DESC1 adapterDesc;
         mHardwareAdapter->GetDesc1(&adapterDesc);
 
@@ -96,6 +104,89 @@
         mSupportedExtensions.EnableExtension(Extension::TimestampQuery);
     }
 
+    MaybeError Adapter::InitializeDebugLayerFilters() {
+        if (!GetInstance()->IsBackendValidationEnabled()) {
+            return {};
+        }
+        ComPtr<ID3D12InfoQueue> infoQueue;
+        ASSERT_SUCCESS(mD3d12Device.As(&infoQueue));
+        // We create storage filter with a deny list to deny specific messages from getting
+        // written to the queue. The filter will silence them in the debug output.
+        D3D12_INFO_QUEUE_FILTER storageFilter = {};
+
+        D3D12_MESSAGE_ID denyIds[] = {
+
+            //
+            // Permanent IDs: list of warnings that are not applicable
+            //
+
+            // Resource sub-allocation partially maps pre-allocated heaps. This means the
+            // entire physical addresses space may have no resources or have many resources
+            // assigned the same heap.
+            D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_HAS_NO_RESOURCE,
+            D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_INTERSECTS_MULTIPLE_BUFFERS,
+
+            // The debug layer validates pipeline objects when they are created. Dawn validates
+            // them when them when they are set. Therefore, since the issue is caught at a later
+            // time, we can silence this warnings.
+            D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET,
+
+            // Adding a clear color during resource creation would require heuristics or delayed
+            // creation.
+            // https://crbug.com/dawn/418
+            D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
+            D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
+
+            // Dawn enforces proper Unmaps at a later time.
+            // https://crbug.com/dawn/422
+            D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED,
+
+            //
+            // Temporary IDs: list of warnings that should be fixed or promoted
+            //
+
+            // Remove after warning have been addressed
+            // https://crbug.com/dawn/419
+            D3D12_MESSAGE_ID_UNMAP_RANGE_NOT_EMPTY,
+
+            // Remove after warning have been addressed
+            // https://crbug.com/dawn/421
+            D3D12_MESSAGE_ID_GPU_BASED_VALIDATION_INCOMPATIBLE_RESOURCE_STATE,
+        };
+
+        storageFilter.DenyList.NumIDs = ARRAYSIZE(denyIds);
+        storageFilter.DenyList.pIDList = denyIds;
+        DAWN_TRY(CheckHRESULT(infoQueue->PushStorageFilter(&storageFilter),
+                              "ID3D12InfoQueue::PushStorageFilter"));
+
+        // We create a retrieval filter with an allow list to select which messages we are
+        // allowed to be read back from the queue. If any messages are read back, they are
+        // converted to Dawn errors.
+        D3D12_INFO_QUEUE_FILTER retrievalFilter{};
+        // We will only create errors from warnings or worse. This ignores info and message.
+        D3D12_MESSAGE_SEVERITY severities[] = {
+            D3D12_MESSAGE_SEVERITY_ERROR,
+            D3D12_MESSAGE_SEVERITY_WARNING,
+            D3D12_MESSAGE_SEVERITY_CORRUPTION,
+        };
+        retrievalFilter.AllowList.NumSeverities = ARRAYSIZE(severities);
+        retrievalFilter.AllowList.pSeverityList = severities;
+        DAWN_TRY(CheckHRESULT(infoQueue->PushRetrievalFilter(&retrievalFilter),
+                              "ID3D12InfoQueue::PushRetrievalFilter"));
+
+        return {};
+    }
+
+    void Adapter::CleanUpDebugLayerFilters() {
+        if (!GetInstance()->IsBackendValidationEnabled()) {
+            return;
+        }
+        ComPtr<ID3D12InfoQueue> infoQueue;
+        ASSERT_SUCCESS(mD3d12Device.As(&infoQueue));
+        infoQueue->PopRetrievalFilter();
+        infoQueue->PopStorageFilter();
+    }
+
     ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) {
         return Device::Create(this, descriptor);
     }
diff --git a/src/dawn_native/d3d12/AdapterD3D12.h b/src/dawn_native/d3d12/AdapterD3D12.h
index 48345c1..e0910bf 100644
--- a/src/dawn_native/d3d12/AdapterD3D12.h
+++ b/src/dawn_native/d3d12/AdapterD3D12.h
@@ -27,7 +27,7 @@
     class Adapter : public AdapterBase {
       public:
         Adapter(Backend* backend, ComPtr<IDXGIAdapter3> hardwareAdapter);
-        ~Adapter() override = default;
+        ~Adapter() override;
 
         const D3D12DeviceInfo& GetDeviceInfo() const;
         IDXGIAdapter3* GetHardwareAdapter() const;
@@ -39,6 +39,8 @@
       private:
         ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override;
         void InitializeSupportedExtensions();
+        MaybeError InitializeDebugLayerFilters();
+        void CleanUpDebugLayerFilters();
 
         ComPtr<IDXGIAdapter3> mHardwareAdapter;
         ComPtr<ID3D12Device> mD3d12Device;
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 7983b98..aea4aca 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -17,6 +17,7 @@
 #include "common/Assert.h"
 #include "dawn_native/BackendConnection.h"
 #include "dawn_native/ErrorData.h"
+#include "dawn_native/Instance.h"
 #include "dawn_native/d3d12/AdapterD3D12.h"
 #include "dawn_native/d3d12/BackendD3D12.h"
 #include "dawn_native/d3d12/BindGroupD3D12.h"
@@ -41,12 +42,16 @@
 #include "dawn_native/d3d12/SwapChainD3D12.h"
 #include "dawn_native/d3d12/TextureD3D12.h"
 
+#include <sstream>
+
 namespace dawn_native { namespace d3d12 {
 
     // TODO(dawn:155): Figure out these values.
     static constexpr uint16_t kShaderVisibleDescriptorHeapSize = 1024;
     static constexpr uint8_t kAttachmentDescriptorHeapSize = 64;
 
+    static constexpr uint64_t kMaxDebugMessagesToPrint = 5;
+
     // static
     ResultOrError<Device*> Device::Create(Adapter* adapter, const DeviceDescriptor* descriptor) {
         Ref<Device> device = AcquireRef(new Device(adapter, descriptor));
@@ -221,6 +226,9 @@
         mUsedComObjectRefs.ClearUpTo(completedSerial);
         DAWN_TRY(ExecutePendingCommandContext());
         DAWN_TRY(NextSerial());
+
+        DAWN_TRY(CheckDebugLayerAndGenerateErrors());
+
         return {};
     }
 
@@ -460,6 +468,51 @@
         return {};
     }
 
+    MaybeError Device::CheckDebugLayerAndGenerateErrors() {
+        if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) {
+            return {};
+        }
+
+        ComPtr<ID3D12InfoQueue> infoQueue;
+        ASSERT_SUCCESS(mD3d12Device.As(&infoQueue));
+        uint64_t totalErrors = infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter();
+
+        // Check if any errors have occurred otherwise we would be creating an empty error. Note
+        // that we use GetNumStoredMessagesAllowedByRetrievalFilter instead of GetNumStoredMessages
+        // because we only convert WARNINGS or higher messages to dawn errors.
+        if (totalErrors == 0) {
+            return {};
+        }
+
+        std::ostringstream messages;
+        uint64_t errorsToPrint = std::min(kMaxDebugMessagesToPrint, totalErrors);
+        for (uint64_t i = 0; i < errorsToPrint; ++i) {
+            SIZE_T messageLength = 0;
+            HRESULT hr = infoQueue->GetMessageW(i, nullptr, &messageLength);
+            if (FAILED(hr)) {
+                messages << " ID3D12InfoQueue::GetMessageW failed with " << hr << '\n';
+                continue;
+            }
+
+            std::unique_ptr<uint8_t[]> messageData(new uint8_t[messageLength]);
+            D3D12_MESSAGE* message = reinterpret_cast<D3D12_MESSAGE*>(messageData.get());
+            hr = infoQueue->GetMessageW(i, message, &messageLength);
+            if (FAILED(hr)) {
+                messages << " ID3D12InfoQueue::GetMessageW failed with " << hr << '\n';
+                continue;
+            }
+
+            messages << message->pDescription << " (" << message->ID << ")\n";
+        }
+        if (errorsToPrint < totalErrors) {
+            messages << (totalErrors - errorsToPrint) << " messages silenced\n";
+        }
+        // We only print up to the first kMaxDebugMessagesToPrint errors
+        infoQueue->ClearStoredMessages();
+
+        return DAWN_INTERNAL_ERROR(messages.str());
+    }
+
     void Device::ShutDownImpl() {
         ASSERT(GetState() == State::Disconnected);
 
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 5f0fafc..7ce3a95 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -156,6 +156,8 @@
         void ShutDownImpl() override;
         MaybeError WaitForIdleForDestruction() override;
 
+        MaybeError CheckDebugLayerAndGenerateErrors();
+
         ComPtr<ID3D12Fence> mFence;
         HANDLE mFenceEvent = nullptr;
         Serial CheckAndUpdateCompletedSerials() override;