Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions fml/platform/darwin/cf_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace fml {
/// Default retain and release implementations for CFRef.
template <typename T>
struct CFRefTraits {
static constexpr T kNullValue = nullptr;
static void Retain(T instance) { CFRetain(instance); }
static void Release(T instance) { CFRelease(instance); }
};
Expand All @@ -26,7 +27,7 @@ template <class T>
class CFRef {
public:
/// Creates a new null CFRef.
CFRef() : instance_(nullptr) {}
CFRef() : instance_(CFRefTraits<T>::kNullValue) {}

/// Takes over ownership of `instance`, which is expected to be already
/// retained.
Expand All @@ -43,7 +44,7 @@ class CFRef {
/// Move ctor: Takes over ownership of the CoreFoundation object owned
/// by `other`. The object owned by `other` is set to null.
CFRef(CFRef&& other) : instance_(other.instance_) {
other.instance_ = nullptr;
other.instance_ = CFRefTraits<T>::kNullValue;
}

/// Takes over ownership of the CoreFoundation object owned by `other`.
Expand All @@ -57,14 +58,14 @@ class CFRef {
if (instance_) {
CFRefTraits<T>::Release(instance_);
}
instance_ = nullptr;
instance_ = CFRefTraits<T>::kNullValue;
}

/// Takes over ownership of `instance`, null by default. The object is
/// expected to be already retained if non-null.
///
/// Releases the previous object, if non-null.
void Reset(T instance = nullptr) {
void Reset(T instance = CFRefTraits<T>::kNullValue) {
if (instance_) {
CFRefTraits<T>::Release(instance_);
}
Expand All @@ -73,7 +74,7 @@ class CFRef {

/// Retains a shared copy of `instance`. The previous object is released if
/// non-null. Has no effect if `instance` is the currently-held object.
void Retain(T instance = nullptr) {
void Retain(T instance = CFRefTraits<T>::kNullValue) {
if (instance_ == instance) {
return;
}
Expand All @@ -88,7 +89,7 @@ class CFRef {
/// with the object.
[[nodiscard]] T Release() {
auto instance = instance_;
instance_ = nullptr;
instance_ = CFRefTraits<T>::kNullValue;
return instance;
}

Expand All @@ -108,7 +109,9 @@ class CFRef {
operator T() const { return instance_; }

/// Returns true if the underlying CoreFoundation object is non-null.
explicit operator bool() const { return instance_ != nullptr; }
explicit operator bool() const {
return instance_ != CFRefTraits<T>::kNullValue;
}

private:
T instance_;
Expand Down
29 changes: 29 additions & 0 deletions fml/platform/darwin/cf_utils_unittests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
#include "flutter/testing/testing.h"

namespace fml {

// Template specialization used in CFTest.SupportsCustomRetainRelease.
template <>
struct CFRefTraits<int64_t> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not proud of what I've done... but I kind of am...

static bool retain_called;
static bool release_called;

static constexpr int64_t kNullValue = 0;
static void Retain(int64_t instance) { retain_called = true; }
static void Release(int64_t instance) { release_called = true; }
};
bool CFRefTraits<int64_t>::retain_called = false;
bool CFRefTraits<int64_t>::release_called = false;

namespace testing {

TEST(CFTest, CanCreateRefs) {
Expand Down Expand Up @@ -83,5 +97,20 @@
EXPECT_EQ(ref_count_before + 1u, ref_count_after);
}

TEST(CFTest, SupportsCustomRetainRelease) {
CFRef<int64_t> ref(1);
ASSERT_EQ(ref.Get(), 1);
ASSERT_FALSE(CFRefTraits<int64_t>::retain_called);
ASSERT_FALSE(CFRefTraits<int64_t>::release_called);
ref.Reset();
ASSERT_EQ(ref.Get(), 0);
ASSERT_TRUE(CFRefTraits<int64_t>::release_called);
ref.Retain(2);
ASSERT_EQ(ref.Get(), 2);
ASSERT_TRUE(CFRefTraits<int64_t>::retain_called);
CFRefTraits<int64_t>::retain_called = false;
CFRefTraits<int64_t>::release_called = false;
}

} // namespace testing
} // namespace fml
3 changes: 2 additions & 1 deletion impeller/golden_tests/metal_screenshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@

namespace fml {

/// Default retain and release implementations for CGImageRef.
/// fml::CFRef retain and release implementations for CGImageRef.
template <>
struct CFRefTraits<CGImageRef> {
static constexpr CGImageRef kNullValue = nullptr;
static void Retain(CGImageRef instance) { CGImageRetain(instance); }
static void Release(CGImageRef instance) { CGImageRelease(instance); }
};
Expand Down
103 changes: 37 additions & 66 deletions shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#import <Foundation/Foundation.h>

#import "flutter/fml/platform/darwin/cf_utils.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/IOKit.h"

Expand Down Expand Up @@ -34,75 +35,42 @@

} // namespace

namespace flutter {
namespace {

#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG || \
FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE

template <typename T>
T ClearValue() {
return nullptr;
}
namespace fml {

/// fml::CFRef retain and release implementations for io_object_t and related types.
template <>
io_object_t ClearValue<io_object_t>() {
return 0;
}
struct CFRefTraits<io_object_t> {
static constexpr io_object_t kNullValue = 0;
static void Retain(io_object_t instance) { IOObjectRetain(instance); }
static void Release(io_object_t instance) { IOObjectRelease(instance); }
};

template <typename T>
/// Generic RAII wrapper like unique_ptr but gives access to its handle.
class Scoped {
public:
typedef void (*Deleter)(T);
explicit Scoped(Deleter deleter) : object_(ClearValue<T>()), deleter_(deleter) {}
Scoped(T object, Deleter deleter) : object_(object), deleter_(deleter) {}
~Scoped() {
if (object_) {
deleter_(object_);
}
}
T* handle() {
if (object_) {
deleter_(object_);
object_ = ClearValue<T>();
}
return &object_;
}
T get() { return object_; }
void reset(T new_value) {
if (object_) {
deleter_(object_);
}
object_ = new_value;
}
} // namespace fml

private:
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(Scoped);
T object_;
Deleter deleter_;
};
#endif

void DeleteCF(CFMutableDictionaryRef value) {
CFRelease(value);
}
namespace flutter {
namespace {

void DeleteIO(io_object_t value) {
IOObjectRelease(value);
}
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG || \
FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE

std::optional<GpuUsageInfo> FindGpuUsageInfo(io_iterator_t iterator) {
for (Scoped<io_registry_entry_t> regEntry(IOIteratorNext(iterator), DeleteIO); regEntry.get();
regEntry.reset(IOIteratorNext(iterator))) {
Scoped<CFMutableDictionaryRef> serviceDictionary(DeleteCF);
if (IORegistryEntryCreateCFProperties(regEntry.get(), serviceDictionary.handle(),
for (fml::CFRef<io_registry_entry_t> reg_entry(IOIteratorNext(iterator)); reg_entry.Get();
reg_entry.Reset(IOIteratorNext(iterator))) {
CFMutableDictionaryRef cf_service_dictionary;
if (IORegistryEntryCreateCFProperties(reg_entry.Get(), &cf_service_dictionary,
kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) {
continue;
}

NSDictionary* dictionary =
((__bridge NSDictionary*)serviceDictionary.get())[@"PerformanceStatistics"];
NSNumber* utilization = dictionary[@"Device Utilization %"];
// Transfer ownership to ARC-managed pointer.
NSDictionary* service_dictionary = (__bridge_transfer NSDictionary*)cf_service_dictionary;
cf_service_dictionary = nullptr;
NSDictionary* performanceStatistics = service_dictionary[@"PerformanceStatistics"];
NSNumber* utilization = performanceStatistics[@"Device Utilization %"];
if (utilization) {
return (GpuUsageInfo){.percent_usage = [utilization doubleValue]};
}
Expand All @@ -111,24 +79,27 @@ void DeleteIO(io_object_t value) {
}

[[maybe_unused]] std::optional<GpuUsageInfo> FindSimulatorGpuUsageInfo() {
Scoped<io_iterator_t> iterator(DeleteIO);
io_iterator_t io_iterator;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching("IntelAccelerator"),
iterator.handle()) == kIOReturnSuccess) {
return FindGpuUsageInfo(iterator.get());
&io_iterator) == kIOReturnSuccess) {
fml::CFRef<io_iterator_t> iterator(io_iterator);
return FindGpuUsageInfo(iterator.Get());
}
return std::nullopt;
}

[[maybe_unused]] std::optional<GpuUsageInfo> FindDeviceGpuUsageInfo() {
Scoped<io_iterator_t> iterator(DeleteIO);
io_iterator_t io_iterator;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching("sgx"),
iterator.handle()) == kIOReturnSuccess) {
for (Scoped<io_registry_entry_t> regEntry(IOIteratorNext(iterator.get()), DeleteIO);
regEntry.get(); regEntry.reset(IOIteratorNext(iterator.get()))) {
Scoped<io_iterator_t> innerIterator(DeleteIO);
if (IORegistryEntryGetChildIterator(regEntry.get(), kIOServicePlane,
innerIterator.handle()) == kIOReturnSuccess) {
std::optional<GpuUsageInfo> result = FindGpuUsageInfo(innerIterator.get());
&io_iterator) == kIOReturnSuccess) {
fml::CFRef<io_iterator_t> iterator(io_iterator);
for (fml::CFRef<io_registry_entry_t> reg_entry(IOIteratorNext(iterator.Get())); reg_entry.Get();
reg_entry.Reset(IOIteratorNext(iterator.Get()))) {
io_iterator_t io_inner_iterator;
if (IORegistryEntryGetChildIterator(reg_entry.Get(), kIOServicePlane, &io_inner_iterator) ==
kIOReturnSuccess) {
fml::CFRef<io_iterator_t> inner_iterator(io_inner_iterator);
std::optional<GpuUsageInfo> result = FindGpuUsageInfo(inner_iterator.Get());
if (result.has_value()) {
return result;
}
Expand Down