Support for resource heap tier 2.

Enables mixing of texture and buffers in the same heap.
This allows better heap re-use and reduces internal fragmentation.
A toggle has been added and enabled by default.

BUG=dawn:27

Change-Id: I466dc96240fe1e8de6e3dc56ed5547d7b61ee045
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12821
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/Toggles.cpp b/src/dawn_native/Toggles.cpp
index 7cc6f35..d3d0e8d 100644
--- a/src/dawn_native/Toggles.cpp
+++ b/src/dawn_native/Toggles.cpp
@@ -70,7 +70,12 @@
                "workaround is enabled by default on all Vulkan drivers to solve an issue in the "
                "Vulkan SPEC about the texture-to-texture copies with compressed formats. See #1005 "
                "(https://github.com/KhronosGroup/Vulkan-Docs/issues/1005) for more details.",
-               "https://bugs.chromium.org/p/dawn/issues/detail?id=42"}}}};
+               "https://bugs.chromium.org/p/dawn/issues/detail?id=42"}},
+             {Toggle::UseD3D12ResourceHeapTier2,
+              {"use_d3d12_resource_heap_tier2",
+               "Enable support for resource heap tier 2. Resource heap tier 2 allows mixing of "
+               "texture and buffers in the same heap. This allows better heap re-use and reduces "
+               "fragmentation."}}}};
 
     }  // anonymous namespace
 
diff --git a/src/dawn_native/Toggles.h b/src/dawn_native/Toggles.h
index d526594..1238450 100644
--- a/src/dawn_native/Toggles.h
+++ b/src/dawn_native/Toggles.h
@@ -30,6 +30,7 @@
         LazyClearResourceOnFirstUse,
         TurnOffVsync,
         UseTemporaryBufferInCompressedTextureToTextureCopy,
+        UseD3D12ResourceHeapTier2,
 
         EnumCount,
         InvalidEnum = EnumCount,
diff --git a/src/dawn_native/d3d12/D3D12Info.cpp b/src/dawn_native/d3d12/D3D12Info.cpp
index 1431b34..edfeb82 100644
--- a/src/dawn_native/d3d12/D3D12Info.cpp
+++ b/src/dawn_native/d3d12/D3D12Info.cpp
@@ -16,7 +16,7 @@
 
 #include "dawn_native/d3d12/AdapterD3D12.h"
 #include "dawn_native/d3d12/BackendD3D12.h"
-
+#include "dawn_native/d3d12/D3D12Error.h"
 #include "dawn_native/d3d12/PlatformFunctions.h"
 
 namespace dawn_native { namespace d3d12 {
@@ -31,12 +31,18 @@
             // for backwards compat.
             // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_feature
             D3D12_FEATURE_DATA_ARCHITECTURE arch = {};
-            if (FAILED(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &arch,
-                                                                sizeof(arch)))) {
-                return DAWN_DEVICE_LOST_ERROR("CheckFeatureSupport failed");
-            }
+            DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(
+                                      D3D12_FEATURE_ARCHITECTURE, &arch, sizeof(arch)),
+                                  "ID3D12Device::CheckFeatureSupport"));
 
             info.isUMA = arch.UMA;
+
+            D3D12_FEATURE_DATA_D3D12_OPTIONS options = {};
+            DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(
+                                      D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)),
+                                  "ID3D12Device::CheckFeatureSupport"));
+
+            info.resourceHeapTier = options.ResourceHeapTier;
         }
 
         return info;
diff --git a/src/dawn_native/d3d12/D3D12Info.h b/src/dawn_native/d3d12/D3D12Info.h
index 11be2d3..1471be0 100644
--- a/src/dawn_native/d3d12/D3D12Info.h
+++ b/src/dawn_native/d3d12/D3D12Info.h
@@ -24,6 +24,7 @@
 
     struct D3D12DeviceInfo {
         bool isUMA;
+        uint32_t resourceHeapTier;
     };
 
     ResultOrError<D3D12DeviceInfo> GatherDeviceInfo(const Adapter& adapter);
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 9976ff5..4a384a0 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -42,6 +42,7 @@
 
     Device::Device(Adapter* adapter, const DeviceDescriptor* descriptor)
         : DeviceBase(adapter, descriptor) {
+        InitTogglesFromDriver();
         if (descriptor != nullptr) {
             ApplyToggleOverrides(descriptor);
         }
@@ -406,4 +407,13 @@
         mD3d11On12DeviceContext->Flush();
     }
 
+    const D3D12DeviceInfo& Device::GetDeviceInfo() const {
+        return ToBackend(GetAdapter())->GetDeviceInfo();
+    }
+
+    void Device::InitTogglesFromDriver() {
+        const bool useResourceHeapTier2 = (GetDeviceInfo().resourceHeapTier >= 2);
+        SetToggle(Toggle::UseD3D12ResourceHeapTier2, useResourceHeapTier2);
+    }
+
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 4aa27ff..8b07313 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -20,6 +20,7 @@
 #include "common/SerialQueue.h"
 #include "dawn_native/Device.h"
 #include "dawn_native/d3d12/CommandRecordingContext.h"
+#include "dawn_native/d3d12/D3D12Info.h"
 #include "dawn_native/d3d12/Forward.h"
 #include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h"
 
@@ -71,6 +72,8 @@
         ResultOrError<CommandRecordingContext*> GetPendingCommandContext();
         Serial GetPendingCommandSerial() const override;
 
+        const D3D12DeviceInfo& GetDeviceInfo() const;
+
         MaybeError NextSerial();
         MaybeError WaitForSerial(Serial serial);
 
@@ -99,6 +102,8 @@
             ID3D12Resource* d3d12Resource);
         void ReleaseKeyedMutexForTexture(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex);
 
+        void InitTogglesFromDriver();
+
       private:
         ResultOrError<BindGroupBase*> CreateBindGroupImpl(
             const BindGroupDescriptor* descriptor) override;
diff --git a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
index 15d8d35..d1707c9 100644
--- a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
+++ b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
@@ -24,12 +24,15 @@
         D3D12_HEAP_TYPE GetD3D12HeapType(ResourceHeapKind resourceHeapKind) {
             switch (resourceHeapKind) {
                 case Readback_OnlyBuffers:
+                case Readback_AllBuffersAndTextures:
                     return D3D12_HEAP_TYPE_READBACK;
+                case Default_AllBuffersAndTextures:
                 case Default_OnlyBuffers:
                 case Default_OnlyNonRenderableOrDepthTextures:
                 case Default_OnlyRenderableOrDepthTextures:
                     return D3D12_HEAP_TYPE_DEFAULT;
                 case Upload_OnlyBuffers:
+                case Upload_AllBuffersAndTextures:
                     return D3D12_HEAP_TYPE_UPLOAD;
                 default:
                     UNREACHABLE();
@@ -38,6 +41,10 @@
 
         D3D12_HEAP_FLAGS GetD3D12HeapFlags(ResourceHeapKind resourceHeapKind) {
             switch (resourceHeapKind) {
+                case Default_AllBuffersAndTextures:
+                case Readback_AllBuffersAndTextures:
+                case Upload_AllBuffersAndTextures:
+                    return D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES;
                 case Default_OnlyBuffers:
                 case Readback_OnlyBuffers:
                 case Upload_OnlyBuffers:
@@ -53,7 +60,21 @@
 
         ResourceHeapKind GetResourceHeapKind(D3D12_RESOURCE_DIMENSION dimension,
                                              D3D12_HEAP_TYPE heapType,
-                                             D3D12_RESOURCE_FLAGS flags) {
+                                             D3D12_RESOURCE_FLAGS flags,
+                                             uint32_t resourceHeapTier) {
+            if (resourceHeapTier >= 2) {
+                switch (heapType) {
+                    case D3D12_HEAP_TYPE_UPLOAD:
+                        return Upload_AllBuffersAndTextures;
+                    case D3D12_HEAP_TYPE_DEFAULT:
+                        return Default_AllBuffersAndTextures;
+                    case D3D12_HEAP_TYPE_READBACK:
+                        return Readback_AllBuffersAndTextures;
+                    default:
+                        UNREACHABLE();
+                }
+            }
+
             switch (dimension) {
                 case D3D12_RESOURCE_DIMENSION_BUFFER: {
                     switch (heapType) {
@@ -90,6 +111,10 @@
     }  // namespace
 
     ResourceAllocatorManager::ResourceAllocatorManager(Device* device) : mDevice(device) {
+        mResourceHeapTier = (mDevice->IsToggleEnabled(Toggle::UseD3D12ResourceHeapTier2))
+                                ? mDevice->GetDeviceInfo().resourceHeapTier
+                                : 1;
+
         for (uint32_t i = 0; i < ResourceHeapKind::EnumCount; i++) {
             const ResourceHeapKind resourceHeapKind = static_cast<ResourceHeapKind>(i);
             mHeapAllocators[i] = std::make_unique<HeapAllocator>(
@@ -153,8 +178,9 @@
 
         const D3D12_RESOURCE_DESC resourceDescriptor = allocation.GetD3D12Resource()->GetDesc();
 
-        const size_t resourceHeapKindIndex = GetResourceHeapKind(
-            resourceDescriptor.Dimension, heapProp.Type, resourceDescriptor.Flags);
+        const size_t resourceHeapKindIndex =
+            GetResourceHeapKind(resourceDescriptor.Dimension, heapProp.Type,
+                                resourceDescriptor.Flags, mResourceHeapTier);
 
         mSubAllocatedResourceAllocators[resourceHeapKindIndex]->Deallocate(allocation);
     }
@@ -163,8 +189,8 @@
         D3D12_HEAP_TYPE heapType,
         const D3D12_RESOURCE_DESC& resourceDescriptor,
         D3D12_RESOURCE_STATES initialUsage) {
-        const size_t resourceHeapKindIndex =
-            GetResourceHeapKind(resourceDescriptor.Dimension, heapType, resourceDescriptor.Flags);
+        const size_t resourceHeapKindIndex = GetResourceHeapKind(
+            resourceDescriptor.Dimension, heapType, resourceDescriptor.Flags, mResourceHeapTier);
 
         BuddyMemoryAllocator* allocator =
             mSubAllocatedResourceAllocators[resourceHeapKindIndex].get();
diff --git a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h
index 4daa07d..d6a3bab 100644
--- a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h
+++ b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h
@@ -26,10 +26,21 @@
 
     class Device;
 
-    // Heap types + flags combinations are named after the D3D constants.
+    // Resource heap types + flags combinations are named after the D3D constants.
     // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_heap_flags
     // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_heap_type
     enum ResourceHeapKind {
+
+        // Resource heap tier 2
+        // Allows resource heaps to contain all buffer and textures types.
+        // This enables better heap re-use by avoiding the need for separate heaps and
+        // also reduces fragmentation.
+        Readback_AllBuffersAndTextures,
+        Upload_AllBuffersAndTextures,
+        Default_AllBuffersAndTextures,
+
+        // Resource heap tier 1
+        // Resource heaps only support types from a single resource category.
         Readback_OnlyBuffers,
         Upload_OnlyBuffers,
         Default_OnlyBuffers,
@@ -70,6 +81,7 @@
             D3D12_RESOURCE_STATES initialUsage);
 
         Device* mDevice;
+        uint32_t mResourceHeapTier;
 
         static constexpr uint64_t kMaxHeapSize = 32ll * 1024ll * 1024ll * 1024ll;  // 32GB
         static constexpr uint64_t kMinHeapSize = 4ll * 1024ll * 1024ll;            // 4MB
diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp
index 0d28cf1..d90528a 100644
--- a/src/tests/end2end/BufferTests.cpp
+++ b/src/tests/end2end/BufferTests.cpp
@@ -648,6 +648,7 @@
 
 DAWN_INSTANTIATE_TEST(CreateBufferMappedTests,
                       D3D12Backend,
+                      ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_resource_heap_tier2"}),
                       MetalBackend,
                       OpenGLBackend,
                       VulkanBackend);
diff --git a/src/tests/end2end/MultisampledRenderingTests.cpp b/src/tests/end2end/MultisampledRenderingTests.cpp
index a8ea16e..cd1b0ee 100644
--- a/src/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/tests/end2end/MultisampledRenderingTests.cpp
@@ -505,6 +505,7 @@
 
 DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
                       D3D12Backend,
+                      ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_resource_heap_tier2"}),
                       MetalBackend,
                       OpenGLBackend,
                       VulkanBackend,