Add ComputeEstimatedMemoryUsage

For Chrome background memory tracing, we need a method to retrieve total
memory usage from Dawn without the overhead of generating a detailed
memory dump. Add ComputeEstimatedMemoryUsage method for that.

Also contains a change to how we report textures in memory dumps -
shared and memoryless textures will be present in the dump but with zero
size so the totals don't change, but we still have useful information
for profiling/debugging.

Bug: 330806170
Change-Id: Ib28a3f69f40fffedbc7ab879e94468aa8c0461a5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/201794
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
diff --git a/include/dawn/native/DawnNative.h b/include/dawn/native/DawnNative.h
index c910f50..594c387 100644
--- a/include/dawn/native/DawnNative.h
+++ b/include/dawn/native/DawnNative.h
@@ -298,6 +298,10 @@
 };
 DAWN_NATIVE_EXPORT void DumpMemoryStatistics(WGPUDevice device, MemoryDump* dump);
 
+// Unlike memory dumps which include detailed information about allocations, this only returns the
+// total estimated memory usage, and is intended for background tracing for UMA.
+DAWN_NATIVE_EXPORT uint64_t ComputeEstimatedMemoryUsage(WGPUDevice device);
+
 }  // namespace dawn::native
 
 #endif  // INCLUDE_DAWN_NATIVE_DAWNNATIVE_H_
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index e2bd22d..db7a0b4 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -1127,10 +1127,7 @@
 }
 
 void BufferBase::DumpMemoryStatistics(MemoryDump* dump, const char* prefix) const {
-    // Do not emit for destroyed buffers.
-    if (!IsAlive()) {
-        return;
-    }
+    DAWN_ASSERT(IsAlive() && !IsError());
     std::string name = absl::StrFormat("%s/buffer_%p", prefix, static_cast<const void*>(this));
     dump->AddScalar(name.c_str(), MemoryDump::kNameSize, MemoryDump::kUnitsBytes,
                     GetAllocatedSize());
diff --git a/src/dawn/native/DawnNative.cpp b/src/dawn/native/DawnNative.cpp
index 699dde0..885e62c 100644
--- a/src/dawn/native/DawnNative.cpp
+++ b/src/dawn/native/DawnNative.cpp
@@ -309,4 +309,8 @@
     FromAPI(device)->DumpMemoryStatistics(dump);
 }
 
+uint64_t ComputeEstimatedMemoryUsage(WGPUDevice device) {
+    return FromAPI(device)->ComputeEstimatedMemoryUsage();
+}
+
 }  // namespace dawn::native
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index d020b97e..3a604dc 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -2613,6 +2613,17 @@
     });
 }
 
+uint64_t DeviceBase::ComputeEstimatedMemoryUsage() const {
+    uint64_t size = 0;
+    GetObjectTrackingList(ObjectType::Texture)->ForEach([&](const ApiObjectBase* texture) {
+        size += static_cast<const TextureBase*>(texture)->ComputeEstimatedByteSize();
+    });
+    GetObjectTrackingList(ObjectType::Buffer)->ForEach([&](const ApiObjectBase* buffer) {
+        size += static_cast<const BufferBase*>(buffer)->GetAllocatedSize();
+    });
+    return size;
+}
+
 ResultOrError<Ref<BufferBase>> DeviceBase::GetOrCreateTemporaryUniformBuffer(size_t size) {
     if (!mTemporaryUniformBuffer || mTemporaryUniformBuffer->GetSize() != size) {
         BufferDescriptor desc;
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index 183ce56..1c5122c 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -457,6 +457,7 @@
     Ref<RenderPipelineBase> AddOrGetCachedRenderPipeline(Ref<RenderPipelineBase> renderPipeline);
 
     void DumpMemoryStatistics(dawn::native::MemoryDump* dump) const;
+    uint64_t ComputeEstimatedMemoryUsage() const;
 
     ResultOrError<Ref<BufferBase>> GetOrCreateTemporaryUniformBuffer(size_t size);
 
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index 978fd78..8d929f5 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -1225,12 +1225,6 @@
 }
 
 void TextureBase::DumpMemoryStatistics(dawn::native::MemoryDump* dump, const char* prefix) const {
-    // Do not emit for destroyed textures, textures that wrap external shared texture memory, or
-    // textures used as transient (memoryless) attachments.
-    if (!IsAlive() || GetSharedResourceMemoryContents() != nullptr ||
-        (GetInternalUsage() & wgpu::TextureUsage::TransientAttachment) != 0) {
-        return;
-    }
     std::string name = absl::StrFormat("%s/texture_%p", prefix, static_cast<const void*>(this));
     dump->AddScalar(name.c_str(), MemoryDump::kNameSize, MemoryDump::kUnitsBytes,
                     ComputeEstimatedByteSize());
@@ -1243,7 +1237,13 @@
 }
 
 uint64_t TextureBase::ComputeEstimatedByteSize() const {
-    DAWN_ASSERT(!IsError());
+    DAWN_ASSERT(IsAlive() && !IsError());
+    // Do not emit a non-zero size for textures that wrap external shared texture memory, or
+    // textures used as transient (memoryless) attachments.
+    if (GetSharedResourceMemoryContents() != nullptr ||
+        (GetInternalUsage() & wgpu::TextureUsage::TransientAttachment) != 0) {
+        return 0;
+    }
     uint64_t byteSize = 0;
     for (Aspect aspect : IterateEnumMask(SelectFormatAspects(*mFormat, wgpu::TextureAspect::All))) {
         const AspectInfo& info = mFormat->GetAspectInfo(aspect);
diff --git a/src/dawn/native/Texture.h b/src/dawn/native/Texture.h
index 2e0cd3e..caad0e2 100644
--- a/src/dawn/native/Texture.h
+++ b/src/dawn/native/Texture.h
@@ -157,6 +157,8 @@
 
     void DumpMemoryStatistics(dawn::native::MemoryDump* dump, const char* prefix) const;
 
+    uint64_t ComputeEstimatedByteSize() const;
+
     // Dawn API
     TextureViewBase* APICreateView(const TextureViewDescriptor* descriptor = nullptr);
     TextureViewBase* APICreateErrorView(const TextureViewDescriptor* descriptor = nullptr);
@@ -194,8 +196,6 @@
 
     std::string GetSizeLabel() const;
 
-    uint64_t ComputeEstimatedByteSize() const;
-
     wgpu::TextureDimension mDimension;
     // Only used for compatibility mode
     wgpu::TextureViewDimension mCompatibilityTextureBindingViewDimension =
diff --git a/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp b/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp
index dbf4592..ceb9a5e 100644
--- a/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp
+++ b/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp
@@ -40,6 +40,7 @@
 namespace {
 
 using ::testing::ByMove;
+using ::testing::DoDefault;
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::StrEq;
@@ -155,12 +156,12 @@
     EXPECT_CALL(memoryDumpMock, AddScalar(StrEq(textureLabel(etc2Texture)), MemoryDump::kNameSize,
                                           MemoryDump::kUnitsBytes, kETC2TextureSize));
 
-    // Create a texture and destroy it and check that its size is not counted.
+    // Create a texture and destroy it and check that its info is not emitted.
     wgpu::Texture destroyedTexture = device.CreateTexture(&kMipmappedTextureDesc);
     EXPECT_TRUE(destroyedTexture);
     destroyedTexture.Destroy();
 
-    // Create a shared resourc memory texture and check that its size is not counted.
+    // Create a shared resource memory texture and check that its size is not counted.
     constexpr wgpu::TextureFormat kRGBA8UnormTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
     const wgpu::TextureDescriptor kSharedTextureDesc = {
         .usage = wgpu::TextureUsage::TextureBinding,
@@ -169,17 +170,41 @@
         .viewFormatCount = 1,
         .viewFormats = &kRGBA8UnormTextureFormat,
     };
-    Ref<TextureMock> textureMock =
+    Ref<TextureMock> sharedTextureMock =
         AcquireRef(new NiceMock<TextureMock>(mDeviceMock, FromCppAPI(&kSharedTextureDesc)));
-    textureMock->SetSharedResourceMemoryContentsForTesting(
+    sharedTextureMock->SetSharedResourceMemoryContentsForTesting(
         AcquireRef(new SharedResourceMemoryContents(nullptr)));
-    EXPECT_CALL(*mDeviceMock, CreateTextureImpl).WillOnce(Return(ByMove(std::move(textureMock))));
+    EXPECT_CALL(*mDeviceMock, CreateTextureImpl)
+        .WillOnce(Return(ByMove(std::move(sharedTextureMock))))
+        .WillRepeatedly(DoDefault());
     wgpu::Texture sharedTexture = device.CreateTexture(&kSharedTextureDesc);
+    EXPECT_CALL(memoryDumpMock, AddScalar(StrEq(textureLabel(sharedTexture)), MemoryDump::kNameSize,
+                                          MemoryDump::kUnitsBytes, /*size=*/0));
+
+    // Create a transient attachment (memoryless) texture and check that its size is not counted.
+    mDeviceMock->ForceEnableFeatureForTesting(Feature::TransientAttachments);
+    const wgpu::TextureDescriptor kTransientAttachmentTextureDesc = {
+        .usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment,
+        .size = {.width = 30, .height = 20},
+        .format = kRGBA8UnormTextureFormat,
+        .viewFormatCount = 1,
+        .viewFormats = &kRGBA8UnormTextureFormat,
+    };
+    wgpu::Texture transientAttachmentTexture =
+        device.CreateTexture(&kTransientAttachmentTextureDesc);
+    EXPECT_CALL(memoryDumpMock,
+                AddScalar(StrEq(textureLabel(transientAttachmentTexture)), MemoryDump::kNameSize,
+                          MemoryDump::kUnitsBytes, /*size=*/0));
 
     DumpMemoryStatistics(device.Get(), &memoryDumpMock);
 
     EXPECT_EQ(memoryDumpMock.GetTotalSize(), kBufferAllocatedSize + kMipmappedTextureSize +
                                                  kMultisampleTextureSize + kETC2TextureSize);
+
+    // Check that ComputeEstimatedMemoryUsage() matches the memory dump total size.
+    EXPECT_EQ(
+        ComputeEstimatedMemoryUsage(device.Get()),
+        kBufferAllocatedSize + kMipmappedTextureSize + kMultisampleTextureSize + kETC2TextureSize);
 }
 
 }  // namespace