Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 0d36bd5

Browse files
[Impeller] track the sizes of all outstanding MTLTexture allocations and report per frame in MB, matching Vulkan implementation. (#53618)
Does what it says! Fixes flutter/flutter#150936
1 parent c0c5665 commit 0d36bd5

File tree

12 files changed

+211
-7
lines changed

12 files changed

+211
-7
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@
176176
../../../flutter/impeller/golden_tests/README.md
177177
../../../flutter/impeller/playground
178178
../../../flutter/impeller/renderer/backend/gles/test
179+
../../../flutter/impeller/renderer/backend/metal/allocator_mtl_unittests.mm
179180
../../../flutter/impeller/renderer/backend/metal/texture_mtl_unittests.mm
180181
../../../flutter/impeller/renderer/backend/vulkan/allocator_vk_unittests.cc
181182
../../../flutter/impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc

impeller/core/allocator.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ class Allocator {
4747
/// @brief Write debug memory usage information to the dart timeline in debug
4848
/// and profile modes.
4949
///
50-
/// This is only supported on the Vulkan backend.
50+
/// This is supported on both the Metal and Vulkan backends.
5151
virtual void DebugTraceMemoryStatistics() const {};
5252

53+
// Visible for testing.
54+
virtual size_t DebugGetHeapUsage() const { return 0; }
55+
5356
protected:
5457
Allocator();
5558

impeller/core/texture_descriptor.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef FLUTTER_IMPELLER_CORE_TEXTURE_DESCRIPTOR_H_
66
#define FLUTTER_IMPELLER_CORE_TEXTURE_DESCRIPTOR_H_
77

8+
#include <cstdint>
89
#include "impeller/core/formats.h"
910
#include "impeller/geometry/size.h"
1011

@@ -51,6 +52,22 @@ struct TextureDescriptor {
5152
return size.Area() * BytesPerPixelForPixelFormat(format);
5253
}
5354

55+
constexpr size_t GetByteSizeOfAllMipLevels() const {
56+
if (!IsValid()) {
57+
return 0u;
58+
}
59+
size_t result = 0u;
60+
int64_t width = size.width;
61+
int64_t height = size.height;
62+
for (auto i = 0u; i < mip_count; i++) {
63+
result +=
64+
ISize(width, height).Area() * BytesPerPixelForPixelFormat(format);
65+
width /= 2;
66+
height /= 2;
67+
}
68+
return result;
69+
}
70+
5471
constexpr size_t GetBytesPerRow() const {
5572
if (!IsValid()) {
5673
return 0u;

impeller/playground/playground_test.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class PlaygroundTest : public Playground,
4242

4343
private:
4444
// |Playground|
45-
bool ShouldKeepRendering() const;
45+
bool ShouldKeepRendering() const override;
4646

4747
#if FML_OS_MACOSX
4848
fml::ScopedNSAutoreleasePool autorelease_pool_;

impeller/renderer/backend/metal/BUILD.gn

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,14 @@ impeller_component("metal") {
6565
impeller_component("metal_unittests") {
6666
testonly = true
6767

68-
sources = [ "texture_mtl_unittests.mm" ]
68+
sources = [
69+
"allocator_mtl_unittests.mm",
70+
"texture_mtl_unittests.mm",
71+
]
6972

7073
deps = [
7174
":metal",
75+
"//flutter/impeller/playground:playground_test",
7276
"//flutter/testing:testing_lib",
7377
]
7478

impeller/renderer/backend/metal/allocator_mtl.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,42 @@
66
#define FLUTTER_IMPELLER_RENDERER_BACKEND_METAL_ALLOCATOR_MTL_H_
77

88
#include <Metal/Metal.h>
9+
#include <atomic>
910

11+
#include "impeller/base/thread.h"
1012
#include "impeller/core/allocator.h"
1113

1214
namespace impeller {
1315

16+
class DebugAllocatorStats {
17+
public:
18+
DebugAllocatorStats() {}
19+
20+
~DebugAllocatorStats() {}
21+
22+
/// Increment the tracked allocation size in bytes.
23+
void Increment(size_t size);
24+
25+
/// Decrement the tracked allocation size in bytes.
26+
void Decrement(size_t size);
27+
28+
/// Get the current tracked allocation size in MB.
29+
size_t GetAllocationSizeMB();
30+
31+
private:
32+
std::atomic<size_t> size_ = 0;
33+
};
34+
1435
class AllocatorMTL final : public Allocator {
1536
public:
1637
AllocatorMTL();
1738

1839
// |Allocator|
1940
~AllocatorMTL() override;
2041

42+
// |Allocator|
43+
size_t DebugGetHeapUsage() const override;
44+
2145
private:
2246
friend class ContextMTL;
2347

@@ -26,6 +50,12 @@ class AllocatorMTL final : public Allocator {
2650
bool supports_memoryless_targets_ = false;
2751
bool supports_uma_ = false;
2852
bool is_valid_ = false;
53+
54+
#ifdef IMPELLER_DEBUG
55+
std::shared_ptr<DebugAllocatorStats> debug_allocater_ =
56+
std::make_shared<DebugAllocatorStats>();
57+
#endif // IMPELLER_DEBUG
58+
2959
ISize max_texture_supported_;
3060

3161
AllocatorMTL(id<MTLDevice> device, std::string label);
@@ -47,6 +77,9 @@ class AllocatorMTL final : public Allocator {
4777
// |Allocator|
4878
ISize GetMaxTextureSizeSupported() const override;
4979

80+
// |Allocator|
81+
void DebugTraceMemoryStatistics() const override;
82+
5083
AllocatorMTL(const AllocatorMTL&) = delete;
5184

5285
AllocatorMTL& operator=(const AllocatorMTL&) = delete;

impeller/renderer/backend/metal/allocator_mtl.mm

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
#include "flutter/fml/build_config.h"
88
#include "flutter/fml/logging.h"
9+
#include "fml/trace_event.h"
910
#include "impeller/base/validation.h"
11+
#include "impeller/core/formats.h"
1012
#include "impeller/renderer/backend/metal/device_buffer_mtl.h"
1113
#include "impeller/renderer/backend/metal/formats_mtl.h"
1214
#include "impeller/renderer/backend/metal/texture_mtl.h"
@@ -88,6 +90,19 @@ static bool SupportsLossyTextureCompression(id<MTLDevice> device) {
8890
#endif
8991
}
9092

93+
void DebugAllocatorStats::Increment(size_t size) {
94+
size_.fetch_add(size, std::memory_order_relaxed);
95+
}
96+
97+
void DebugAllocatorStats::Decrement(size_t size) {
98+
size_.fetch_sub(size, std::memory_order_relaxed);
99+
}
100+
101+
size_t DebugAllocatorStats::GetAllocationSizeMB() {
102+
size_t new_value = size_ / 1000000;
103+
return new_value;
104+
}
105+
91106
AllocatorMTL::AllocatorMTL(id<MTLDevice> device, std::string label)
92107
: device_(device), allocator_label_(std::move(label)) {
93108
if (!device_) {
@@ -212,11 +227,23 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode,
212227
}
213228
}
214229

230+
#ifdef IMPELLER_DEBUG
231+
if (desc.storage_mode != StorageMode::kDeviceTransient) {
232+
debug_allocater_->Increment(desc.GetByteSizeOfAllMipLevels());
233+
}
234+
#endif // IMPELLER_DEBUG
235+
215236
auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc];
216237
if (!texture) {
217238
return nullptr;
218239
}
219-
return TextureMTL::Create(desc, texture);
240+
std::shared_ptr<TextureMTL> result_texture =
241+
TextureMTL::Create(desc, texture);
242+
#ifdef IMPELLER_DEBUG
243+
result_texture->SetDebugAllocator(debug_allocater_);
244+
#endif // IMPELLER_DEBUG
245+
246+
return result_texture;
220247
}
221248

222249
uint16_t AllocatorMTL::MinimumBytesPerRow(PixelFormat format) const {
@@ -228,4 +255,21 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode,
228255
return max_texture_supported_;
229256
}
230257

258+
size_t AllocatorMTL::DebugGetHeapUsage() const {
259+
#ifdef IMPELLER_DEBUG
260+
return debug_allocater_->GetAllocationSizeMB();
261+
#else
262+
return 0u;
263+
#endif // IMPELLER_DEBUG
264+
}
265+
266+
void AllocatorMTL::DebugTraceMemoryStatistics() const {
267+
#ifdef IMPELLER_DEBUG
268+
size_t allocated_size = DebugGetHeapUsage();
269+
FML_TRACE_COUNTER("flutter", "AllocatorMTL",
270+
reinterpret_cast<int64_t>(this), // Trace Counter ID
271+
"MemoryBudgetUsageMB", allocated_size);
272+
#endif // IMPELLER_DEBUG
273+
}
274+
231275
} // namespace impeller
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/testing/testing.h"
6+
#include "impeller/core/formats.h"
7+
#include "impeller/core/texture_descriptor.h"
8+
#include "impeller/playground/playground_test.h"
9+
#include "impeller/renderer/backend/metal/allocator_mtl.h"
10+
#include "impeller/renderer/backend/metal/context_mtl.h"
11+
#include "impeller/renderer/backend/metal/formats_mtl.h"
12+
#include "impeller/renderer/backend/metal/lazy_drawable_holder.h"
13+
#include "impeller/renderer/backend/metal/texture_mtl.h"
14+
#include "impeller/renderer/capabilities.h"
15+
16+
#include <QuartzCore/CAMetalLayer.h>
17+
#include <memory>
18+
#include <thread>
19+
20+
#include "gtest/gtest.h"
21+
22+
namespace impeller {
23+
namespace testing {
24+
25+
using AllocatorMTLTest = PlaygroundTest;
26+
INSTANTIATE_METAL_PLAYGROUND_SUITE(AllocatorMTLTest);
27+
28+
TEST_P(AllocatorMTLTest, DebugTraceMemoryStatistics) {
29+
auto& context_mtl = ContextMTL::Cast(*GetContext());
30+
const auto& allocator = context_mtl.GetResourceAllocator();
31+
32+
EXPECT_EQ(allocator->DebugGetHeapUsage(), 0u);
33+
34+
// Memoryless texture does not increase allocated size.
35+
{
36+
TextureDescriptor desc;
37+
desc.format = PixelFormat::kR8G8B8A8UNormInt;
38+
desc.storage_mode = StorageMode::kDeviceTransient;
39+
desc.size = {1000, 1000};
40+
auto texture_1 = allocator->CreateTexture(desc);
41+
42+
EXPECT_EQ(allocator->DebugGetHeapUsage(), 0u);
43+
44+
// Private storage texture increases allocated size.
45+
desc.storage_mode = StorageMode::kDevicePrivate;
46+
auto texture_2 = allocator->CreateTexture(desc);
47+
48+
#ifdef IMPELLER_DEBUG
49+
EXPECT_EQ(allocator->DebugGetHeapUsage(), 4u);
50+
#else
51+
EXPECT_EQ(allocator->DebugGetHeapUsage(), 0u);
52+
#endif // IMPELLER_DEBUG
53+
54+
// Host storage texture increases allocated size.
55+
desc.storage_mode = StorageMode::kHostVisible;
56+
auto texture_3 = allocator->CreateTexture(desc);
57+
58+
#ifdef IMPELLER_DEBUG
59+
EXPECT_EQ(allocator->DebugGetHeapUsage(), 8u);
60+
#else
61+
EXPECT_EQ(allocator->DebugGetHeapUsage(), 0u);
62+
#endif // IMPELLER_DEBUG
63+
}
64+
65+
// After all textures are out of scope, memory has been decremented.
66+
EXPECT_EQ(allocator->DebugGetHeapUsage(), 0u);
67+
}
68+
69+
} // namespace testing
70+
} // namespace impeller

impeller/renderer/backend/metal/surface_mtl.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ - (void)flutterPrepareForPresent:(nonnull id<MTLCommandBuffer>)commandBuffer;
229229
return false;
230230
}
231231

232+
#ifdef IMPELLER_DEBUG
233+
context->GetResourceAllocator()->DebugTraceMemoryStatistics();
234+
#endif // IMPELLER_DEBUG
235+
232236
if (requires_blit_) {
233237
if (!(source_texture_ && destination_texture_)) {
234238
return false;

impeller/renderer/backend/metal/texture_mtl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "impeller/base/backend_cast.h"
1111
#include "impeller/core/texture.h"
12+
#include "impeller/renderer/backend/metal/allocator_mtl.h"
1213

1314
namespace impeller {
1415

@@ -47,7 +48,16 @@ class TextureMTL final : public Texture,
4748

4849
bool GenerateMipmap(id<MTLBlitCommandEncoder> encoder);
4950

51+
#ifdef IMPELLER_DEBUG
52+
void SetDebugAllocator(
53+
const std::shared_ptr<DebugAllocatorStats>& debug_allocator);
54+
#endif // IMPELLER_DEBUG
55+
5056
private:
57+
#ifdef IMPELLER_DEBUG
58+
std::shared_ptr<DebugAllocatorStats> debug_allocator_ = nullptr;
59+
#endif // IMPELLER_DEBUG
60+
5161
AcquireTextureProc aquire_proc_ = {};
5262
bool is_valid_ = false;
5363
bool is_wrapped_ = false;

impeller/renderer/backend/metal/texture_mtl.mm

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <memory>
77

88
#include "impeller/base/validation.h"
9+
#include "impeller/core/formats.h"
910
#include "impeller/core/texture_descriptor.h"
1011

1112
namespace impeller {
@@ -59,7 +60,17 @@ new TextureMTL(
5960
return std::make_shared<TextureMTL>(desc, [texture]() { return texture; });
6061
}
6162

62-
TextureMTL::~TextureMTL() = default;
63+
TextureMTL::~TextureMTL() {
64+
#ifdef IMPELLER_DEBUG
65+
if (debug_allocator_) {
66+
auto desc = GetTextureDescriptor();
67+
if (desc.storage_mode == StorageMode::kDeviceTransient) {
68+
return;
69+
}
70+
debug_allocator_->Decrement(desc.GetByteSizeOfBaseMipLevel());
71+
}
72+
#endif // IMPELLER_DEBUG
73+
}
6374

6475
void TextureMTL::SetLabel(std::string_view label) {
6576
if (is_drawable_) {
@@ -76,6 +87,13 @@ new TextureMTL(
7687
return OnSetContents(mapping->GetMapping(), mapping->GetSize(), slice);
7788
}
7889

90+
#ifdef IMPELLER_DEBUG
91+
void TextureMTL::SetDebugAllocator(
92+
const std::shared_ptr<DebugAllocatorStats>& debug_allocator) {
93+
debug_allocator_ = debug_allocator;
94+
}
95+
#endif // IMPELLER_DEBUG
96+
7997
// |Texture|
8098
bool TextureMTL::OnSetContents(const uint8_t* contents,
8199
size_t length,

impeller/renderer/backend/vulkan/allocator_vk.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ class AllocatorVK final : public Allocator {
2121
// |Allocator|
2222
~AllocatorVK() override;
2323

24-
// Visible for testing
25-
size_t DebugGetHeapUsage() const;
24+
// |Allocator|
25+
size_t DebugGetHeapUsage() const override;
2626

2727
/// @brief Select a matching memory type for the given
2828
/// [memory_type_bits_requirement], or -1 if none is found.

0 commit comments

Comments
 (0)