docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Use Mali system metrics in the Profiler

    You can use the System Metrics Mali package in the Profiler in the following ways:

    • Use the Mali System Metrics Profiler module
    • Create a custom Profiler module in the Editor
    • Create a custom Profiler module with the ProfilerModule API

    Use the Mali System Metrics Profiler module

    The System Metrics Mali package includes a Profiler module to monitor low-level GPU metrics in the Unity Profiler window.

    Module Editor Module
    The Mali System Metrics module.

    The chart view displays a high level overview of how the GPU distributed its processing load.

    The module's Details view, displays detailed information about the selected frame, such as information about tiling, memory usage, Z tests, and shader instructions. For descriptions of the individual counters, you can hover over their name in the Unity Editor or refer to Mali GPU counters reference.

    Create a custom Profiler module in the Editor

    Use the Profiler Module Editor to build Profiler modules directly in the Editor that use the Mali stats. A custom module can display additional Mali metrics in a Profiler chart, alongside other built-in or custom Profiler counters.

    Connect the Profiler to a Mali GPU device and capture profiling data before you open the Profiler Module Editor. If you open the Profiler Module Editor window first, the Mali metrics counters don't appear in the Available Counters menu.

    Module Editor Module
    The Mali Shader Usage module.

    Modules you create with the Module Editor aren't part of your Unity project and only appear in your local Editor. To define a custom Profiler Module that's available for all project users, refer to Create a custom Profiler module with the ProfilerModule API.

    Create a custom Profiler module with the ProfilerModule API

    Use the ProfilerModule API to define a custom Profiler module that uses the Mali counters. You can use this to combine the Mali metrics with other built-in or custom Profiler counters in one module, or to implement a bespoke visualization using the Mali performance data.

    The following example uses the Mali metrics alongside custom Profiler counters to correlate the metrics on a single chart.

    Custom Profiler Module
    Custom Profiler Module.

    using Unity.Profiling.Editor;
    using Unity.Profiling.LowLevel.Unsafe;
    using Unity.Profiling.SystemMetrics;
    
    [ProfilerModuleMetadata("Mali Tanks")]
    public class MaliTanksProfilerModule : ProfilerModule
    {
        static readonly ProfilerCounterDescriptor[] k_ChartCounters =
        {
            GetDescriptorProfilerCounterHandle(SystemMetricsMali.Instance.GpuCycles),
            GetDescriptorProfilerCounterHandle(SystemMetricsMali.Instance.GpuVertexAndComputeCycles),
            GetDescriptorProfilerCounterHandle(SystemMetricsMali.Instance.GpuFragmentCycles),
            GetDescriptorProfilerCounterHandle(SystemMetricsMali.Instance.GpuShaderCoreUtilization),
            GetDescriptorProfilerCounterHandle(SystemMetricsMali.Instance.GpuMemoryReadBytes),
            new ProfilerCounterDescriptor("Tank Count", Complete.GameStats.TanksProfilerCategory.Name),
            new ProfilerCounterDescriptor("Bullet Count", Complete.GameStats.TanksProfilerCategory.Name),
        };
    
        public MaliTanksProfilerModule() : base(k_ChartCounters) { }
    
        static ProfilerCounterDescriptor GetDescriptorProfilerCounterHandle(ProfilerRecorderHandle handle)
        {
            var description = ProfilerRecorderHandle.GetDescription(handle);
            return new ProfilerCounterDescriptor(description.Name, description.Category);
        }
    }
    

    Access the Profiler counters through code

    The System Metrics Mali package implements all its metrics as Profiler counters. You can access them through a script using the ProfilerRecorder API.

    All Mali counters belong to the same ProfilerCategory, which you can access via SystemMetricsMali.Instance.Category.

    The following example uses the ProfilerRecorder API to display some Mali metrics in-game on the screen, including from within a Release build without the Profiler attached.

    In-game display
    In-game display.

    using System.Collections.Generic;
    using System.Text;
    using Unity.Profiling;
    using Unity.Profiling.LowLevel.Unsafe;
    using Unity.Profiling.SystemMetrics;
    using UnityEngine;
    
    public class MaliHUD : MonoBehaviour
    {
        List<HUDEntry> m_Entries;
        string m_Text;
        GUIStyle m_TextStyle;
        StringBuilder m_TextBuilder;
    
        void Awake()
        {
            m_TextBuilder = new StringBuilder(500);
            m_Entries = new List<HUDEntry>()
            {
                new HUDEntry(SystemMetricsMali.Instance.GpuShaderCoreCycles),
                new HUDEntry(SystemMetricsMali.Instance.GpuShaderArithmeticCycles),
                new HUDEntry(SystemMetricsMali.Instance.GpuShaderLoadStoreCycles),
                new HUDEntry(SystemMetricsMali.Instance.GpuShaderTextureCycles),
            };
        }
    
        void OnEnable()
        {
            foreach (var entry in m_Entries)
                entry.Recorder.Start();
        }
    
        void OnDisable()
        {
            foreach (var entry in m_Entries)
                entry.Recorder.Dispose();
        }
    
        void Update()
        {
            m_TextBuilder.Clear();
    
            foreach (var entry in m_Entries)
            {
                var value = entry.Recorder.LastValue;
                m_TextBuilder.AppendLine($"{entry.Name}: {FormatCount(value)}");
            }
    
            m_Text = m_TextBuilder.ToString();
        }
    
        void OnGUI()
        {
            if (m_TextStyle == null)
                m_TextStyle = new GUIStyle(GUI.skin.label) { fontSize = 42 };
    
            GUILayout.Label(m_Text, m_TextStyle);
        }
    
        static string FormatCount(in long count)
        {
            if (count < 1000)
                return string.Format("{0:D}", count);
            else if (count < 1000000) // 1e6
                return string.Format("{0:F2}k", count * 1.0e-3);
            else
                return string.Format("{0:F2}M", count * 1.0e-6);
        }
    
        struct HUDEntry
        {
            public HUDEntry(ProfilerRecorderHandle handle)
            {
                Recorder = new ProfilerRecorder(handle);
                if (!Recorder.Valid)
                    throw new System.ArgumentException();
    
                var description = ProfilerRecorderHandle.GetDescription(handle);
                Name = description.Name;
            }
    
            public string Name { get; }
            public ProfilerRecorder Recorder { get; }
        }
    }
    

    Discover available counters at runtime

    You can also discover which Mali counters are available on a specific device at runtime using the GetAvailableCounters method. This is useful when you want to adapt your profiling based on the specific Mali GPU capabilities.

    using System.Collections.Generic;
    using Unity.Profiling;
    using Unity.Profiling.LowLevel.Unsafe;
    using Unity.Profiling.SystemMetrics;
    using UnityEngine;
    
    public class MaliCounterDiscovery : MonoBehaviour
    {
        void Start()
        {
            if (!SystemMetricsMali.Instance.Active)
            {
                Debug.Log("Mali counters not available on this device");
                return;
            }
    
            // Get all available counters on this device
            var availableCounters = new List<ProfilerRecorderHandle>();
            SystemMetricsMali.Instance.GetAvailableCounters(availableCounters);
    
            Debug.Log($"Found {availableCounters.Count} available Mali counters:");
    
            foreach (var handle in availableCounters)
            {
                var description = ProfilerRecorderHandle.GetDescription(handle);
                Debug.Log($"- {description.Name} (Category: {description.Category})");
            }
    
            // Create recorders for all available counters
            var recorders = new List<ProfilerRecorder>();
            foreach (var handle in availableCounters)
            {
                var recorder = new ProfilerRecorder(handle);
                if (recorder.Valid)
                {
                    recorder.Start();
                    recorders.Add(recorder);
                }
            }
    
            // Use the recorders to collect data...
            // Remember to dispose them when done:
            // foreach (var recorder in recorders) recorder.Dispose();
        }
    }
    

    Additional resources

    • Connecting the Profiler to a data source
    • Customizing Profiler modules
    • Adding profiling information to your code
    In This Article
    Back to top
    Copyright © 2025 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)