Resource Management 5: D3D support for resource allocation.

Refactor existing resource allocators by adding a memory type
and memory handle.

BUG=dawn:27, dawn:153

Change-Id: I090b6ab40e7eaa0d7ea5ce1e8b760e961be9b559
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9420
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/Forward.h b/src/dawn_native/Forward.h
index 1ba29da..ad73bef 100644
--- a/src/dawn_native/Forward.h
+++ b/src/dawn_native/Forward.h
@@ -36,6 +36,7 @@
     class RenderBundleEncoderBase;
     class RenderPassEncoderBase;
     class RenderPipelineBase;
+    class ResourceHeapBase;
     class SamplerBase;
     class ShaderModuleBase;
     class StagingBufferBase;
diff --git a/src/dawn_native/ResourceHeap.h b/src/dawn_native/ResourceHeap.h
new file mode 100644
index 0000000..c4d6707
--- /dev/null
+++ b/src/dawn_native/ResourceHeap.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_RESOURCEHEAP_H_
+#define DAWNNATIVE_RESOURCEHEAP_H_
+
+#include "dawn_native/Error.h"
+
+namespace dawn_native {
+
+    // Wrapper for a resource backed by a heap.
+    class ResourceHeapBase {
+      protected:
+        ResourceHeapBase() = default;
+        virtual ~ResourceHeapBase() = default;
+    };
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_RESOURCEHEAP_H_
\ No newline at end of file
diff --git a/src/dawn_native/ResourceMemoryAllocation.cpp b/src/dawn_native/ResourceMemoryAllocation.cpp
new file mode 100644
index 0000000..1ace4d4
--- /dev/null
+++ b/src/dawn_native/ResourceMemoryAllocation.cpp
@@ -0,0 +1,53 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_native/ResourceMemoryAllocation.h"
+#include "common/Assert.h"
+
+#include <limits>
+
+namespace dawn_native {
+
+    static constexpr uint64_t INVALID_OFFSET = std::numeric_limits<uint64_t>::max();
+
+    ResourceMemoryAllocation::ResourceMemoryAllocation()
+        : mMethod(AllocationMethod::kInvalid), mOffset(INVALID_OFFSET), mResourceHeap(nullptr) {
+    }
+
+    ResourceMemoryAllocation::ResourceMemoryAllocation(uint64_t offset,
+                                                       ResourceHeapBase* resourceHeap,
+                                                       AllocationMethod method)
+        : mMethod(method), mOffset(offset), mResourceHeap(resourceHeap) {
+    }
+
+    ResourceHeapBase* ResourceMemoryAllocation::GetResourceHeap() const {
+        ASSERT(mMethod != AllocationMethod::kInvalid);
+        return mResourceHeap;
+    }
+
+    uint64_t ResourceMemoryAllocation::GetOffset() const {
+        ASSERT(mMethod != AllocationMethod::kInvalid);
+        return mOffset;
+    }
+
+    AllocationMethod ResourceMemoryAllocation::GetAllocationMethod() const {
+        ASSERT(mMethod != AllocationMethod::kInvalid);
+        return mMethod;
+    }
+
+    void ResourceMemoryAllocation::Invalidate() {
+        mResourceHeap = nullptr;
+        mMethod = AllocationMethod::kInvalid;
+    }
+}  // namespace dawn_native
\ No newline at end of file
diff --git a/src/dawn_native/ResourceMemoryAllocation.h b/src/dawn_native/ResourceMemoryAllocation.h
new file mode 100644
index 0000000..4e69a22
--- /dev/null
+++ b/src/dawn_native/ResourceMemoryAllocation.h
@@ -0,0 +1,60 @@
+// Copyright 2018 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_RESOURCEMEMORYALLOCATION_H_
+#define DAWNNATIVE_RESOURCEMEMORYALLOCATION_H_
+
+#include <cstdint>
+
+namespace dawn_native {
+
+    class ResourceHeapBase;
+
+    // Allocation method determines how memory was sub-divided.
+    // Used by the device to get the allocator that was responsible for the allocation.
+    enum class AllocationMethod {
+
+        // Memory not sub-divided.
+        kDirect,
+
+        // Memory sub-divided using one or more blocks of various sizes.
+        kSubAllocated,
+
+        // Memory not allocated or freed.
+        kInvalid
+    };
+
+    // Handle into a resource heap pool.
+    class ResourceMemoryAllocation {
+      public:
+        ResourceMemoryAllocation();
+        ResourceMemoryAllocation(uint64_t offset,
+                                 ResourceHeapBase* resourceHeap,
+                                 AllocationMethod method);
+        ~ResourceMemoryAllocation() = default;
+
+        ResourceHeapBase* GetResourceHeap() const;
+        uint64_t GetOffset() const;
+        AllocationMethod GetAllocationMethod() const;
+
+        void Invalidate();
+
+      private:
+        AllocationMethod mMethod;
+        uint64_t mOffset;
+        ResourceHeapBase* mResourceHeap;
+    };
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_RESOURCEMEMORYALLOCATION_H_
\ No newline at end of file
diff --git a/src/dawn_native/ToBackend.h b/src/dawn_native/ToBackend.h
index 4f11fd4..b9940ab 100644
--- a/src/dawn_native/ToBackend.h
+++ b/src/dawn_native/ToBackend.h
@@ -74,6 +74,11 @@
     };
 
     template <typename BackendTraits>
+    struct ToBackendTraits<ResourceHeapBase, BackendTraits> {
+        using BackendType = typename BackendTraits::ResourceHeapType;
+    };
+
+    template <typename BackendTraits>
     struct ToBackendTraits<SamplerBase, BackendTraits> {
         using BackendType = typename BackendTraits::SamplerType;
     };
diff --git a/src/dawn_native/d3d12/BufferD3D12.cpp b/src/dawn_native/d3d12/BufferD3D12.cpp
index 585c24f..148a740 100644
--- a/src/dawn_native/d3d12/BufferD3D12.cpp
+++ b/src/dawn_native/d3d12/BufferD3D12.cpp
@@ -18,7 +18,7 @@
 #include "common/Constants.h"
 #include "common/Math.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
-#include "dawn_native/d3d12/ResourceAllocator.h"
+#include "dawn_native/d3d12/ResourceHeapD3D12.h"
 
 namespace dawn_native { namespace d3d12 {
 
@@ -71,6 +71,9 @@
 
     Buffer::Buffer(Device* device, const BufferDescriptor* descriptor)
         : BufferBase(device, descriptor) {
+    }
+
+    MaybeError Buffer::Initialize() {
         D3D12_RESOURCE_DESC resourceDescriptor;
         resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
         resourceDescriptor.Alignment = 0;
@@ -105,8 +108,11 @@
             mLastUsage = dawn::BufferUsage::CopySrc;
         }
 
-        mResource =
-            device->GetResourceAllocator()->Allocate(heapType, resourceDescriptor, bufferUsage);
+        DAWN_TRY_ASSIGN(
+            mResourceAllocation,
+            ToBackend(GetDevice())
+                ->AllocateMemory(heapType, resourceDescriptor, bufferUsage, D3D12_HEAP_FLAG_NONE));
+        return {};
     }
 
     Buffer::~Buffer() {
@@ -118,8 +124,8 @@
         return Align(GetSize(), 256);
     }
 
-    ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {
-        return mResource;
+    ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() const {
+        return ToBackend(mResourceAllocation.GetResourceHeap())->GetD3D12Resource();
     }
 
     // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
@@ -174,7 +180,7 @@
 
         barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
         barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
-        barrier->Transition.pResource = mResource.Get();
+        barrier->Transition.pResource = GetD3D12Resource().Get();
         barrier->Transition.StateBefore = lastState;
         barrier->Transition.StateAfter = newState;
         barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
@@ -192,7 +198,7 @@
     }
 
     D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const {
-        return mResource->GetGPUVirtualAddress();
+        return ToBackend(mResourceAllocation.GetResourceHeap())->GetGPUPointer();
     }
 
     void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite) {
@@ -210,8 +216,8 @@
 
     MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) {
         mWrittenMappedRange = {0, GetSize()};
-        ASSERT_SUCCESS(
-            mResource->Map(0, &mWrittenMappedRange, reinterpret_cast<void**>(mappedPointer)));
+        ASSERT_SUCCESS(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
+                                               reinterpret_cast<void**>(mappedPointer)));
         return {};
     }
 
@@ -219,8 +225,7 @@
         mWrittenMappedRange = {};
         D3D12_RANGE readRange = {0, GetSize()};
         char* data = nullptr;
-        ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
-
+        ASSERT_SUCCESS(GetD3D12Resource()->Map(0, &readRange, reinterpret_cast<void**>(&data)));
         // There is no need to transition the resource to a new state: D3D12 seems to make the GPU
         // writes available when the fence is passed.
         MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
@@ -231,8 +236,8 @@
     MaybeError Buffer::MapWriteAsyncImpl(uint32_t serial) {
         mWrittenMappedRange = {0, GetSize()};
         char* data = nullptr;
-        ASSERT_SUCCESS(mResource->Map(0, &mWrittenMappedRange, reinterpret_cast<void**>(&data)));
-
+        ASSERT_SUCCESS(
+            GetD3D12Resource()->Map(0, &mWrittenMappedRange, reinterpret_cast<void**>(&data)));
         // There is no need to transition the resource to a new state: D3D12 seems to make the CPU
         // writes available on queue submission.
         MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
@@ -241,14 +246,12 @@
     }
 
     void Buffer::UnmapImpl() {
-        mResource->Unmap(0, &mWrittenMappedRange);
-        ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource);
+        GetD3D12Resource()->Unmap(0, &mWrittenMappedRange);
         mWrittenMappedRange = {};
     }
 
     void Buffer::DestroyImpl() {
-        ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource);
-        mResource = nullptr;
+        ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation);
     }
 
     MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
diff --git a/src/dawn_native/d3d12/BufferD3D12.h b/src/dawn_native/d3d12/BufferD3D12.h
index 1b8b177..7a9b433 100644
--- a/src/dawn_native/d3d12/BufferD3D12.h
+++ b/src/dawn_native/d3d12/BufferD3D12.h
@@ -18,6 +18,7 @@
 #include "common/SerialQueue.h"
 #include "dawn_native/Buffer.h"
 
+#include "dawn_native/ResourceMemoryAllocation.h"
 #include "dawn_native/d3d12/d3d12_platform.h"
 
 namespace dawn_native { namespace d3d12 {
@@ -29,8 +30,10 @@
         Buffer(Device* device, const BufferDescriptor* descriptor);
         ~Buffer();
 
+        MaybeError Initialize();
+
         uint32_t GetD3D12Size() const;
-        ComPtr<ID3D12Resource> GetD3D12Resource();
+        ComPtr<ID3D12Resource> GetD3D12Resource() const;
         D3D12_GPU_VIRTUAL_ADDRESS GetVA() const;
         void OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite);
         bool TransitionUsageAndGetResourceBarrier(D3D12_RESOURCE_BARRIER* barrier,
@@ -48,7 +51,7 @@
         bool IsMapWritable() const override;
         virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
 
-        ComPtr<ID3D12Resource> mResource;
+        ResourceMemoryAllocation mResourceAllocation;
         bool mFixedResourceState = false;
         dawn::BufferUsage mLastUsage = dawn::BufferUsage::None;
         Serial mLastUsedSerial = UINT64_MAX;
diff --git a/src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.cpp b/src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.cpp
new file mode 100644
index 0000000..772f5d2
--- /dev/null
+++ b/src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.cpp
@@ -0,0 +1,52 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_native/d3d12/CommittedResourceAllocatorD3D12.h"
+#include "dawn_native/d3d12/DeviceD3D12.h"
+#include "dawn_native/d3d12/ResourceHeapD3D12.h"
+
+namespace dawn_native { namespace d3d12 {
+
+    CommittedResourceAllocator::CommittedResourceAllocator(Device* device, D3D12_HEAP_TYPE heapType)
+        : mDevice(device), mHeapType(heapType) {
+    }
+
+    ResultOrError<ResourceMemoryAllocation> CommittedResourceAllocator::Allocate(
+        const D3D12_RESOURCE_DESC& resourceDescriptor,
+        D3D12_RESOURCE_STATES initialUsage,
+        D3D12_HEAP_FLAGS heapFlags) {
+        D3D12_HEAP_PROPERTIES heapProperties;
+        heapProperties.Type = mHeapType;
+        heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+        heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+        heapProperties.CreationNodeMask = 0;
+        heapProperties.VisibleNodeMask = 0;
+
+        ComPtr<ID3D12Resource> committedResource;
+        if (FAILED(mDevice->GetD3D12Device()->CreateCommittedResource(
+                &heapProperties, heapFlags, &resourceDescriptor, initialUsage, nullptr,
+                IID_PPV_ARGS(&committedResource)))) {
+            return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate resource");
+        }
+
+        return ResourceMemoryAllocation(
+            /*offset*/ 0, new ResourceHeap(std::move(committedResource)),
+            AllocationMethod::kDirect);
+    }
+
+    void CommittedResourceAllocator::Deallocate(ResourceMemoryAllocation& allocation) {
+        std::unique_ptr<ResourceHeap> resourceHeap(ToBackend(allocation.GetResourceHeap()));
+        mDevice->ReferenceUntilUnused(resourceHeap->GetD3D12Resource());
+    }
+}}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.h b/src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.h
new file mode 100644
index 0000000..419d1c6
--- /dev/null
+++ b/src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_D3D12_COMMITTEDRESOURCEALLOCATORD3D12_H_
+#define DAWNNATIVE_D3D12_COMMITTEDRESOURCEALLOCATORD3D12_H_
+
+#include "common/SerialQueue.h"
+#include "dawn_native/Error.h"
+#include "dawn_native/ResourceMemoryAllocation.h"
+#include "dawn_native/d3d12/d3d12_platform.h"
+
+namespace dawn_native { namespace d3d12 {
+
+    class Device;
+
+    // Wrapper to allocate D3D12 committed resource.
+    // Committed resources are implicitly backed by a D3D12 heap.
+    class CommittedResourceAllocator {
+      public:
+        CommittedResourceAllocator(Device* device, D3D12_HEAP_TYPE heapType);
+        ~CommittedResourceAllocator() = default;
+
+        ResultOrError<ResourceMemoryAllocation> Allocate(
+            const D3D12_RESOURCE_DESC& resourceDescriptor,
+            D3D12_RESOURCE_STATES initialUsage,
+            D3D12_HEAP_FLAGS heapFlags);
+        void Deallocate(ResourceMemoryAllocation& allocation);
+
+      private:
+        Device* mDevice;
+        D3D12_HEAP_TYPE mHeapType;
+    };
+
+}}  // namespace dawn_native::d3d12
+
+#endif  // DAWNNATIVE_D3D12_COMMITTEDRESOURCEALLOCATORD3D12_H_
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 1f23108..16554b8 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -31,6 +31,7 @@
 #include "dawn_native/d3d12/QueueD3D12.h"
 #include "dawn_native/d3d12/RenderPipelineD3D12.h"
 #include "dawn_native/d3d12/ResourceAllocator.h"
+#include "dawn_native/d3d12/ResourceHeapD3D12.h"
 #include "dawn_native/d3d12/SamplerD3D12.h"
 #include "dawn_native/d3d12/ShaderModuleD3D12.h"
 #include "dawn_native/d3d12/StagingBufferD3D12.h"
@@ -110,12 +111,17 @@
         }
         NextSerial();
         WaitForSerial(mLastSubmittedSerial);  // Wait for all in-flight commands to finish executing
-        TickImpl();                    // Call tick one last time so resources are cleaned up
+        TickImpl();                           // Call tick one last time so resources are cleaned up
 
         // Free services explicitly so that they can free D3D12 resources before destruction of the
         // device.
         mDynamicUploader = nullptr;
 
+        // GPU is no longer executing commands. Existing objects do not get freed until the device
+        // is destroyed. To ensure objects are always released, force the completed serial to be
+        // MAX.
+        mCompletedSerial = std::numeric_limits<Serial>::max();
+
         // Releasing the uploader enqueues buffers to be released.
         // Call Tick() again to clear them before releasing the allocator.
         mResourceAllocator->Tick(mCompletedSerial);
@@ -124,6 +130,8 @@
             ::CloseHandle(mFenceEvent);
         }
 
+        mUsedComObjectRefs.ClearUpTo(mCompletedSerial);
+
         ASSERT(mUsedComObjectRefs.Empty());
         ASSERT(mPendingCommands.commandList == nullptr);
     }
@@ -264,7 +272,9 @@
         return new BindGroupLayout(this, descriptor);
     }
     ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
-        return new Buffer(this, descriptor);
+        std::unique_ptr<Buffer> buffer = std::make_unique<Buffer>(this, descriptor);
+        DAWN_TRY(buffer->Initialize());
+        return buffer.release();
     }
     CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder,
                                                    const CommandBufferDescriptor* descriptor) {
@@ -326,4 +336,48 @@
         return {};
     }
 
+    size_t Device::GetD3D12HeapTypeToIndex(D3D12_HEAP_TYPE heapType) const {
+        ASSERT(heapType > 0);
+        ASSERT(heapType <= kNumHeapTypes);
+        return heapType - 1;
+    }
+
+    void Device::DeallocateMemory(ResourceMemoryAllocation& allocation) {
+        CommittedResourceAllocator* allocator = nullptr;
+        D3D12_HEAP_PROPERTIES heapProp;
+        ToBackend(allocation.GetResourceHeap())
+            ->GetD3D12Resource()
+            ->GetHeapProperties(&heapProp, nullptr);
+        const size_t heapTypeIndex = GetD3D12HeapTypeToIndex(heapProp.Type);
+        ASSERT(heapTypeIndex < kNumHeapTypes);
+        allocator = mDirectResourceAllocators[heapTypeIndex].get();
+        allocator->Deallocate(allocation);
+
+        // Invalidate the underlying resource heap in case the client accidentally
+        // calls DeallocateMemory again using the same allocation.
+        allocation.Invalidate();
+    }
+
+    ResultOrError<ResourceMemoryAllocation> Device::AllocateMemory(
+        D3D12_HEAP_TYPE heapType,
+        const D3D12_RESOURCE_DESC& resourceDescriptor,
+        D3D12_RESOURCE_STATES initialUsage,
+        D3D12_HEAP_FLAGS heapFlags) {
+        const size_t heapTypeIndex = GetD3D12HeapTypeToIndex(heapType);
+        ASSERT(heapTypeIndex < kNumHeapTypes);
+
+        // Get the direct allocator using a tightly sized heap (aka CreateCommittedResource).
+        CommittedResourceAllocator* allocator = mDirectResourceAllocators[heapTypeIndex].get();
+        if (allocator == nullptr) {
+            mDirectResourceAllocators[heapTypeIndex] =
+                std::make_unique<CommittedResourceAllocator>(this, heapType);
+            allocator = mDirectResourceAllocators[heapTypeIndex].get();
+        }
+
+        ResourceMemoryAllocation allocation;
+        DAWN_TRY_ASSIGN(allocation,
+                        allocator->Allocate(resourceDescriptor, initialUsage, heapFlags));
+
+        return allocation;
+    }
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index e45e811..847fe35 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -19,6 +19,7 @@
 
 #include "common/SerialQueue.h"
 #include "dawn_native/Device.h"
+#include "dawn_native/d3d12/CommittedResourceAllocatorD3D12.h"
 #include "dawn_native/d3d12/Forward.h"
 #include "dawn_native/d3d12/d3d12_platform.h"
 
@@ -81,6 +82,14 @@
                                            uint64_t destinationOffset,
                                            uint64_t size) override;
 
+        ResultOrError<ResourceMemoryAllocation> AllocateMemory(
+            D3D12_HEAP_TYPE heapType,
+            const D3D12_RESOURCE_DESC& resourceDescriptor,
+            D3D12_RESOURCE_STATES initialUsage,
+            D3D12_HEAP_FLAGS heapFlags);
+
+        void DeallocateMemory(ResourceMemoryAllocation& allocation);
+
       private:
         ResultOrError<BindGroupBase*> CreateBindGroupImpl(
             const BindGroupDescriptor* descriptor) override;
@@ -104,6 +113,8 @@
             TextureBase* texture,
             const TextureViewDescriptor* descriptor) override;
 
+        size_t GetD3D12HeapTypeToIndex(D3D12_HEAP_TYPE heapType) const;
+
         Serial mCompletedSerial = 0;
         Serial mLastSubmittedSerial = 0;
         ComPtr<ID3D12Fence> mFence;
@@ -128,6 +139,20 @@
         std::unique_ptr<MapRequestTracker> mMapRequestTracker;
         std::unique_ptr<ResourceAllocator> mResourceAllocator;
 
+        static constexpr uint32_t kNumHeapTypes = 4u;  // Number of D3D12_HEAP_TYPE
+
+        static_assert(D3D12_HEAP_TYPE_READBACK <= kNumHeapTypes,
+                      "Readback heap type enum exceeds max heap types");
+        static_assert(D3D12_HEAP_TYPE_UPLOAD <= kNumHeapTypes,
+                      "Upload heap type enum exceeds max heap types");
+        static_assert(D3D12_HEAP_TYPE_DEFAULT <= kNumHeapTypes,
+                      "Default heap type enum exceeds max heap types");
+        static_assert(D3D12_HEAP_TYPE_CUSTOM <= kNumHeapTypes,
+                      "Custom heap type enum exceeds max heap types");
+
+        std::array<std::unique_ptr<CommittedResourceAllocator>, kNumHeapTypes>
+            mDirectResourceAllocators;
+
         dawn_native::PCIInfo mPCIInfo;
     };
 
diff --git a/src/dawn_native/d3d12/Forward.h b/src/dawn_native/d3d12/Forward.h
index ade12e3..f42f824 100644
--- a/src/dawn_native/d3d12/Forward.h
+++ b/src/dawn_native/d3d12/Forward.h
@@ -29,6 +29,7 @@
     class PipelineLayout;
     class Queue;
     class RenderPipeline;
+    class ResourceHeap;
     class Sampler;
     class ShaderModule;
     class StagingBuffer;
@@ -47,6 +48,7 @@
         using PipelineLayoutType = PipelineLayout;
         using QueueType = Queue;
         using RenderPipelineType = RenderPipeline;
+        using ResourceHeapType = ResourceHeap;
         using SamplerType = Sampler;
         using ShaderModuleType = ShaderModule;
         using StagingBufferType = StagingBuffer;
diff --git a/src/dawn_native/d3d12/ResourceHeapD3D12.cpp b/src/dawn_native/d3d12/ResourceHeapD3D12.cpp
new file mode 100644
index 0000000..5aec4b3
--- /dev/null
+++ b/src/dawn_native/d3d12/ResourceHeapD3D12.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_native/d3d12/ResourceHeapD3D12.h"
+#include "dawn_native/d3d12/DeviceD3D12.h"
+
+namespace dawn_native { namespace d3d12 {
+
+    ResourceHeap::ResourceHeap(ComPtr<ID3D12Resource> resource) : mResource(resource) {
+    }
+
+    ComPtr<ID3D12Resource> ResourceHeap::GetD3D12Resource() const {
+        return mResource;
+    }
+
+    D3D12_GPU_VIRTUAL_ADDRESS ResourceHeap::GetGPUPointer() const {
+        return mResource->GetGPUVirtualAddress();
+    }
+}}  // namespace dawn_native::d3d12
\ No newline at end of file
diff --git a/src/dawn_native/d3d12/ResourceHeapD3D12.h b/src/dawn_native/d3d12/ResourceHeapD3D12.h
new file mode 100644
index 0000000..18b342a
--- /dev/null
+++ b/src/dawn_native/d3d12/ResourceHeapD3D12.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_D3D12_RESOURCEHEAPD3D12_H_
+#define DAWNNATIVE_D3D12_RESOURCEHEAPD3D12_H_
+
+#include "dawn_native/ResourceHeap.h"
+#include "dawn_native/d3d12/d3d12_platform.h"
+
+namespace dawn_native { namespace d3d12 {
+
+    // Wrapper for physical memory used with or without a resource object.
+    class ResourceHeap : public ResourceHeapBase {
+      public:
+        ResourceHeap(ComPtr<ID3D12Resource> resource);
+
+        ~ResourceHeap() = default;
+
+        ComPtr<ID3D12Resource> GetD3D12Resource() const;
+        D3D12_GPU_VIRTUAL_ADDRESS GetGPUPointer() const;
+
+      private:
+        ComPtr<ID3D12Resource> mResource;
+    };
+}}  // namespace dawn_native::d3d12
+
+#endif  // DAWNNATIVE_D3D12_RESOURCEHEAPD3D12_H_
\ No newline at end of file
diff --git a/src/dawn_native/d3d12/StagingBufferD3D12.cpp b/src/dawn_native/d3d12/StagingBufferD3D12.cpp
index 6b6adfa..cab3a18 100644
--- a/src/dawn_native/d3d12/StagingBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/StagingBufferD3D12.cpp
@@ -14,7 +14,7 @@
 
 #include "dawn_native/d3d12/StagingBufferD3D12.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
-#include "dawn_native/d3d12/ResourceAllocator.h"
+#include "dawn_native/d3d12/ResourceHeapD3D12.h"
 
 namespace dawn_native { namespace d3d12 {
 
@@ -36,12 +36,11 @@
         resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
         resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE;
 
-        mUploadHeap = mDevice->GetResourceAllocator()->Allocate(
-            D3D12_HEAP_TYPE_UPLOAD, resourceDescriptor, D3D12_RESOURCE_STATE_GENERIC_READ);
+        DAWN_TRY_ASSIGN(mUploadHeap, mDevice->AllocateMemory(
+                                         D3D12_HEAP_TYPE_UPLOAD, resourceDescriptor,
+                                         D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_HEAP_FLAG_NONE));
 
-        // TODO(bryan.bernhart@intel.com): Record the GPU pointer for generic non-upload usage.
-
-        if (FAILED(mUploadHeap->Map(0, nullptr, &mMappedPointer))) {
+        if (FAILED(GetResource()->Map(0, nullptr, &mMappedPointer))) {
             return DAWN_DEVICE_LOST_ERROR("Unable to map staging buffer.");
         }
 
@@ -50,14 +49,14 @@
 
     StagingBuffer::~StagingBuffer() {
         // Invalidate the CPU virtual address & flush cache (if needed).
-        mUploadHeap->Unmap(0, nullptr);
+        GetResource()->Unmap(0, nullptr);
         mMappedPointer = nullptr;
 
-        mDevice->GetResourceAllocator()->Release(mUploadHeap);
+        mDevice->DeallocateMemory(mUploadHeap);
     }
 
     ID3D12Resource* StagingBuffer::GetResource() const {
-        return mUploadHeap.Get();
+        return ToBackend(mUploadHeap.GetResourceHeap())->GetD3D12Resource().Get();
     }
 
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/StagingBufferD3D12.h b/src/dawn_native/d3d12/StagingBufferD3D12.h
index b689df4..633be53 100644
--- a/src/dawn_native/d3d12/StagingBufferD3D12.h
+++ b/src/dawn_native/d3d12/StagingBufferD3D12.h
@@ -15,6 +15,7 @@
 #ifndef DAWNNATIVE_STAGINGBUFFERD3D12_H_
 #define DAWNNATIVE_STAGINGBUFFERD3D12_H_
 
+#include "dawn_native/ResourceMemoryAllocation.h"
 #include "dawn_native/StagingBuffer.h"
 #include "dawn_native/d3d12/d3d12_platform.h"
 
@@ -33,7 +34,7 @@
 
       private:
         Device* mDevice;
-        ComPtr<ID3D12Resource> mUploadHeap;
+        ResourceMemoryAllocation mUploadHeap;
     };
 }}  // namespace dawn_native::d3d12