{"$schema": "https://raw.githubusercontent.com/guitaripod/wwdc-sessions/master/schema/transcript.schema.json", "id": "wwdc2026-388", "language": "eng", "source": "https://developer.apple.com/videos/play/wwdc2026/388", "wordCount": 2852, "segments": [{"start": 7.0, "text": "Hello, and welcome."}, {"start": 8.0, "text": "I'm Ruiwei, an engineer with the Metal Tools Team."}, {"start": 12.0, "text": "A smooth, consistent frame rate"}, {"start": 14.0, "text": "is one of the most important things you can deliver to your players."}, {"start": 18.0, "text": "It is what makes your game feel responsive and polished -"}, {"start": 22.0, "text": "and maintaining that smoothness across an entire play session is key."}, {"start": 27.0, "text": "I'll demonstrate new tools and workflows that give you everything you need"}, {"start": 31.0, "text": "to quickly collect and identify issues in long game sessions."}, {"start": 36.0, "text": "Achieving a great, smooth game experience is a cycle,"}, {"start": 40.0, "text": "you need to play test your game on many different devices and form factors,"}, {"start": 45.0, "text": "collect performance data, analyze the results,"}, {"start": 49.0, "text": "identify performance issues, and fix them."}, {"start": 53.0, "text": "Then you play test again and repeat the cycle"}, {"start": 56.0, "text": "until you hit your performance targets."}, {"start": 59.0, "text": "As players play your game for hours, many conditions could change."}, {"start": 63.0, "text": "The device may heat up and thermal state shifts."}, {"start": 67.0, "text": "The player adjusts graphics settings."}, {"start": 70.0, "text": "They move between different levels and fights."}, {"start": 74.0, "text": "To truly achieve a smooth experience, you need to measure across"}, {"start": 77.0, "text": "many different scenarios over long periods of time."}, {"start": 81.0, "text": "To show you how, I will start off with a brief introduction"}, {"start": 85.0, "text": "to the Metal performance metrics that are available to you."}, {"start": 89.0, "text": "Then, I will show you new tools and workflows that collect"}, {"start": 93.0, "text": "and analyze traces over long periods of time."}, {"start": 97.0, "text": "Next, I will demonstrate how to contextualize the traces,"}, {"start": 101.0, "text": "so you understand the state of your game at any point in time."}, {"start": 106.0, "text": "And finally, how to collect data in the field after your game ships."}, {"start": 111.0, "text": "I will start with Metal performance metrics."}, {"start": 115.0, "text": "Metal tools provide a wide range of different metrics."}, {"start": 119.0, "text": "These metrics give you a baseline to compare between different sessions"}, {"start": 123.0, "text": "and identify potential issues."}, {"start": 125.0, "text": "For example, timing related metrics such as frames per second,"}, {"start": 130.0, "text": "GPU time, and Frame Interval tell you the pacing of your frames"}, {"start": 134.0, "text": "and how utilized the GPU is."}, {"start": 137.0, "text": "On the other hand, metrics such as layer sizes, composition mode,"}, {"start": 142.0, "text": "and MetalFX related metrics help you make sure"}, {"start": 146.0, "text": "the display related settings are set up correctly."}, {"start": 149.0, "text": "There’s one place where you can take a quick glance at all of these metrics."}, {"start": 155.0, "text": "The Metal Performance HUD displays performance metrics such as FPS,"}, {"start": 159.0, "text": "memory usage, and frame interval in an overlay on top of the game's content."}, {"start": 165.0, "text": "It gives you a broad sense of how your game is performing as you play,"}, {"start": 170.0, "text": "and it has a number of customization features."}, {"start": 173.0, "text": "For example, the configuration panel"}, {"start": 175.0, "text": "allows you to customize the list of metrics to display."}, {"start": 179.0, "text": "You can enable or disable each individual metric,"}, {"start": 182.0, "text": "or choose from a preset."}, {"start": 185.0, "text": "You can learn more about each metric"}, {"start": 187.0, "text": "and the HUD in the Metal tools documentation."}, {"start": 190.0, "text": "\"Monitoring your Metal app's graphics performance\""}, {"start": 192.0, "text": "and \"Understanding the Metal Performance HUD metrics\"."}, {"start": 196.0, "text": "While the HUD is great for getting a quick read during development,"}, {"start": 200.0, "text": "it doesn't give you the ability to save the metrics for long durations."}, {"start": 204.0, "text": "It’s easy to forget exactly where you encountered frame drops,"}, {"start": 208.0, "text": "especially when playing for minutes or hours."}, {"start": 212.0, "text": "To get that level of detail, you need to collect performance traces"}, {"start": 216.0, "text": "over long periods of time."}, {"start": 218.0, "text": "For testing your game at desk,"}, {"start": 220.0, "text": "you can use the Game Performance Overview template in Instruments."}, {"start": 225.0, "text": "To start, choose the Game Performance Overview template"}, {"start": 229.0, "text": "in the Instruments template selector."}, {"start": 231.0, "text": "It captures aggregated Metal performance metrics"}, {"start": 234.0, "text": "as well as Time Profiler CPU samples."}, {"start": 238.0, "text": "You can either start your game using Instruments,"}, {"start": 242.0, "text": "or attach to an already running game session on connected devices."}, {"start": 247.0, "text": "Once ready, simply click the record button."}, {"start": 251.0, "text": "This is ideal for data collection while testing your game at your desk -"}, {"start": 255.0, "text": "sessions that last minutes or longer."}, {"start": 258.0, "text": "But here's something really powerful."}, {"start": 261.0, "text": "While your game runs, the system is always recording"}, {"start": 265.0, "text": "and saving Metal performance and resource usage metrics."}, {"start": 270.0, "text": "Aggregated and optional per-frame metrics, such as CPU, GPU, FPS, and Memory"}, {"start": 277.0, "text": "are efficiently stored, and saved for days."}, {"start": 281.0, "text": "This means for a long game session that lasts hours,"}, {"start": 285.0, "text": "and includes multiple state changes,"}, {"start": 287.0, "text": "you can look back in time and collect data after the game session wraps up -"}, {"start": 292.0, "text": "locally, on both macOS and iOS devices."}, {"start": 297.0, "text": "I'll demonstrate how this works."}, {"start": 300.0, "text": "On macOS, you can collect the data through metalperftrace,"}, {"start": 304.0, "text": "a new command line tool in macOS 27."}, {"start": 307.0, "text": "No extra configuration needed."}, {"start": 310.0, "text": "Simply run metalperftace collect"}, {"start": 313.0, "text": "and pass --last for the time duration you want, from hours to days."}, {"start": 319.0, "text": "For example, this command collects a trace"}, {"start": 322.0, "text": "containing performance metrics for the last 5 hours."}, {"start": 326.0, "text": "Alternatively, you can pass an explicit start and end date"}, {"start": 331.0, "text": "to collect the trace for a specific time range."}, {"start": 335.0, "text": "That's how you can look-back and collect traces on macOS."}, {"start": 339.0, "text": "On iOS, look-back collection requires a one time device setup."}, {"start": 344.0, "text": "With developer mode enabled,"}, {"start": 346.0, "text": "go to developer settings and Enable Performance Trace."}, {"start": 350.0, "text": "Select Lookback Collection, and choose for how far back the trace will cover."}, {"start": 356.0, "text": "As a last step, go to control center, and add the Performance Trace button."}, {"start": 363.0, "text": "Now the device is ready for trace collection."}, {"start": 367.0, "text": "You can start play test the game as usual."}, {"start": 370.0, "text": "After it wraps up, bring up the control center."}, {"start": 374.0, "text": "At the tap of the performance trace button, the device will start collecting"}, {"start": 378.0, "text": "and processing the trace for the configured time duration."}, {"start": 383.0, "text": "This might take some time,"}, {"start": 384.0, "text": "and you will receive a notification once processing is done."}, {"start": 388.0, "text": "The collected traces will appear in the Available Trace Files list."}, {"start": 393.0, "text": "And from there you can easily transfer the trace to your Mac for further analysis."}, {"start": 398.0, "text": "Now that I have demonstrated how to collect traces -"}, {"start": 402.0, "text": "both with Instruments and on iOS and macOS devices,"}, {"start": 406.0, "text": "it's time for the next step: analyzing them."}, {"start": 409.0, "text": "On macOS, you can either print or export an overview of the metrics"}, {"start": 414.0, "text": "through metalperftrace."}, {"start": 415.0, "text": "Or open the traces in Instruments and explore the data visually."}, {"start": 420.0, "text": "Let's check both in action."}, {"start": 422.0, "text": "To take a look at the overall performance of a recorded trace,"}, {"start": 426.0, "text": "simply run metalperftrace overview with the trace file."}, {"start": 430.0, "text": "metalperftrace will aggregate and print the overall performance data"}, {"start": 434.0, "text": "of the game sessions contained in the trace."}, {"start": 437.0, "text": "The report is divided into 2 sections for each process."}, {"start": 441.0, "text": "The first section contains the resource usage statistics,"}, {"start": 445.0, "text": "such as memory usage, CPU time, and disk activity."}, {"start": 449.0, "text": "The second section contains Metal performance metrics for each layer."}, {"start": 453.0, "text": "For example,"}, {"start": 455.0, "text": "During this session, the average memory usage is around 2.1GB."}, {"start": 461.0, "text": "The average FPS is about 60."}, {"start": 465.0, "text": "And the report also includes statistical values for metrics"}, {"start": 469.0, "text": "such as frame time and on-GPU time."}, {"start": 472.0, "text": "This gives me a quick idea of the overall performance of the session."}, {"start": 478.0, "text": "If there were multiple game sessions or Metal graphics apps in the trace,"}, {"start": 483.0, "text": "an optional predicate can be used to print data for the exact process you want."}, {"start": 490.0, "text": "Additionally, you can also pass --json to make metalperftrace output in JSON format."}, {"start": 497.0, "text": "Now you get structured output that can be easily fed into scripts"}, {"start": 502.0, "text": "for regression testing,"}, {"start": 503.0, "text": "or AI agents to automatically triage potential performance issues."}, {"start": 509.0, "text": "Instruments can also open and visualize the traces for deeper analysis"}, {"start": 513.0, "text": "with more detailed metrics."}, {"start": 516.0, "text": "The data is plotted on a timeline,"}, {"start": 519.0, "text": "and Instruments automatically evaluates the values"}, {"start": 522.0, "text": "so that stats that deviate too much are highlighted in a different color."}, {"start": 527.0, "text": "For example, in this trace, the FPS dropped for a period of time"}, {"start": 532.0, "text": "and is marked in yellow."}, {"start": 534.0, "text": "The detail view shows statistical values:"}, {"start": 537.0, "text": "min, max, average, and standard deviation for each metric."}, {"start": 542.0, "text": "These stats are aggregated for the whole trace"}, {"start": 545.0, "text": "or the selected time range."}, {"start": 547.0, "text": "You can select a time range"}, {"start": 550.0, "text": "and Instruments will automatically aggregate"}, {"start": 553.0, "text": "and update the stats for the selected duration."}, {"start": 556.0, "text": "Here the average FPS dropped to 26 for the duration,"}, {"start": 561.0, "text": "and GPU usage is really low."}, {"start": 564.0, "text": "This is where I need to dive deeper and try to reproduce an issue,"}, {"start": 569.0, "text": "but there is a big challenge."}, {"start": 571.0, "text": "I know the frame rate drop happened at around the 12-minute mark,"}, {"start": 576.0, "text": "but I do not know what my game was doing at that point."}, {"start": 580.0, "text": "When looking at a trace like this"}, {"start": 582.0, "text": "there is not enough actionable context to investigate further"}, {"start": 585.0, "text": "with just an average FPS."}, {"start": 588.0, "text": "Was the drop due to a specific area of the level?"}, {"start": 592.0, "text": "Or did graphics settings change?"}, {"start": 594.0, "text": "This is why context matters when trying"}, {"start": 596.0, "text": "to identify potential performance issues and analyze traces."}, {"start": 601.0, "text": "To investigate individual problems, you need granular data"}, {"start": 605.0, "text": "that tells you what the game was doing at each moment."}, {"start": 608.0, "text": "So next, I will introduce a new API that helps you to add context to the traces."}, {"start": 615.0, "text": "StateReporting is a new API that lets you describe your game's behavior"}, {"start": 620.0, "text": "and state over time."}, {"start": 621.0, "text": "And it is based on four core concepts."}, {"start": 625.0, "text": "It all starts with a domain."}, {"start": 627.0, "text": "Domains are finite state machines"}, {"start": 630.0, "text": "that represent specific areas of functionality."}, {"start": 634.0, "text": "For example, you can define a level domain that tracks the player progress."}, {"start": 639.0, "text": "Each domain can be in one state at a time."}, {"start": 643.0, "text": "Each state starts with a label, such as \"Level 1\"."}, {"start": 648.0, "text": "It can also include optional stable metadata."}, {"start": 651.0, "text": "This is a immutable dictionary that includes any kind of serializable info"}, {"start": 656.0, "text": "about the current state, in addition to the label."}, {"start": 661.0, "text": "Each state can also include volatile metadata."}, {"start": 664.0, "text": "As the name suggests,"}, {"start": 666.0, "text": "you use it for values that can change within a state,"}, {"start": 670.0, "text": "like the health of the player."}, {"start": 673.0, "text": "As the player progress through the game, you can transition between states"}, {"start": 676.0, "text": "with different label and metadata."}, {"start": 680.0, "text": "For example, transition from \"Level 1\" to \"Level 2\""}, {"start": 683.0, "text": "when the player advances to the next level."}, {"start": 687.0, "text": "Putting this on a timeline, you can define multiple domains"}, {"start": 691.0, "text": "to describe the state of specific areas of the game."}, {"start": 695.0, "text": "For example, a level domain that tracks level changes,"}, {"start": 700.0, "text": "a graphics domain that tracks current graphics settings,"}, {"start": 703.0, "text": "and a network domain for the network status."}, {"start": 707.0, "text": "If we go back to the previous FPS Graph,"}, {"start": 711.0, "text": "now we can contextualize the trace"}, {"start": 714.0, "text": "and I can immediately notice areas that needs improvement,"}, {"start": 718.0, "text": "and focus my optimization efforts."}, {"start": 722.0, "text": "The StateReporting API is available for both Swift and Objective-C,"}, {"start": 727.0, "text": "and it is easy to adopt."}, {"start": 730.0, "text": "I'll show you some sample code that reports the current level of the game."}, {"start": 734.0, "text": "First, create a domain, typically a reverse DNS string,"}, {"start": 739.0, "text": "and ask for a reporter for the domain."}, {"start": 741.0, "text": "Reporters are instances of the state machine"}, {"start": 744.0, "text": "that can be used to report state transitions."}, {"start": 747.0, "text": "To report a state, use reportTransition with a label"}, {"start": 751.0, "text": "to describe the current state."}, {"start": 754.0, "text": "Optionally, to include additional structured info about the state,"}, {"start": 758.0, "text": "simply pass a dictionary in the stableMetadata argument."}, {"start": 762.0, "text": "For volatile metadata,"}, {"start": 764.0, "text": "you can update values without transitioning to another state"}, {"start": 767.0, "text": "by calling reportVolatileMetadataUpdate, with a dictionary representing the info."}, {"start": 774.0, "text": "StateReporting is also tightly integrated with all of the tools I've discussed:"}, {"start": 780.0, "text": "Metal Performance HUD, metalperftrace, and Instruments."}, {"start": 785.0, "text": "In the Metal Performance HUD,"}, {"start": 787.0, "text": "the list of state domains appears in the metrics configuration tab."}, {"start": 791.0, "text": "Enable them and the overlay will display the label, stable and volatile metadata."}, {"start": 798.0, "text": "This is the most direct way of checking your StateReporting adoption."}, {"start": 803.0, "text": "For example, here I'm reporting the level state with a biome"}, {"start": 807.0, "text": "and id as the stable metadata,"}, {"start": 810.0, "text": "and player position as the volatile metadata."}, {"start": 814.0, "text": "The volatile player position is reported once per second,"}, {"start": 817.0, "text": "and the overlay allows me to check the position"}, {"start": 820.0, "text": "as the player moves around."}, {"start": 822.0, "text": "In metalperftrace, when printing the overview"}, {"start": 825.0, "text": "for traces containing state transitions,"}, {"start": 828.0, "text": "the report will contain a list of domains,"}, {"start": 831.0, "text": "the number of transitions, and the last known state."}, {"start": 835.0, "text": "To know the full state transition details, simply pass --include-state-transitions."}, {"start": 842.0, "text": "This will print the full list of states with start and end timestamps."}, {"start": 848.0, "text": "metalperftrace can also automatically aggregate and report metrics"}, {"start": 852.0, "text": "as a function of these states."}, {"start": 855.0, "text": "You can ask metalperftrace to aggregate for all domains and state transitions,"}, {"start": 861.0, "text": "or aggregate for all state transitions for a specific domain,"}, {"start": 865.0, "text": "or aggregate for a specific state label within a domain."}, {"start": 870.0, "text": "For example, to see the average FPS when the graphics is set to high,"}, {"start": 875.0, "text": "I can simply ask metalperftrace to aggregate the \"High\" state label"}, {"start": 879.0, "text": "for the graphics domain."}, {"start": 882.0, "text": "The report will show detailed state info, how long it was active,"}, {"start": 887.0, "text": "and a list of overlapping states in other domains."}, {"start": 891.0, "text": "For example, here the average FPS is only about 24"}, {"start": 895.0, "text": "when the graphics is set to High."}, {"start": 898.0, "text": "To get a more visual look at the state transitions over time,"}, {"start": 902.0, "text": "you can open the trace in Instruments."}, {"start": 905.0, "text": "When opening a trace with state transitions,"}, {"start": 908.0, "text": "Instruments will create a track for each domain"}, {"start": 912.0, "text": "as part of the Points of Interest instrument."}, {"start": 916.0, "text": "Each track graphs state transitions and volatile updates,"}, {"start": 919.0, "text": "making it easy to understand the context."}, {"start": 923.0, "text": "You can select individual states,"}, {"start": 925.0, "text": "and inspect the details of that state in the sidebar,"}, {"start": 929.0, "text": "such as the stable and volatile metadata."}, {"start": 932.0, "text": "Combined with the Metal performance metrics,"}, {"start": 935.0, "text": "I confirmed that the frame rate started dropping"}, {"start": 937.0, "text": "as graphics setting changed to high."}, {"start": 940.0, "text": "That's where I'm going to focus my investigation next."}, {"start": 944.0, "text": "Once there is enough context to reproduce the issue,"}, {"start": 947.0, "text": "the next step is taking a deeper look at specific points of the game."}, {"start": 952.0, "text": "You can capture detailed CPU and GPU scheduling data"}, {"start": 956.0, "text": "by using the Metal system trace template in Instruments."}, {"start": 960.0, "text": "You can also capture and profile frames by using the Metal debugger in Xcode."}, {"start": 966.0, "text": "To learn more about those tools, check \"Metal Developer Tools\" documentation,"}, {"start": 972.0, "text": "and \"Discover new Metal profiling tools for M3 and A17 Pro\"."}, {"start": 977.0, "text": "There are a few best practices to keep in mind"}, {"start": 980.0, "text": "when you adopt StateReporting in your game."}, {"start": 984.0, "text": "First, carefully design your domains and states before instrumenting."}, {"start": 988.0, "text": "Each domain should be conceptually orthogonal to the others."}, {"start": 992.0, "text": "Don't try to represent too many dimensions in a single domain -"}, {"start": 996.0, "text": "that makes it hard to keep track of."}, {"start": 999.0, "text": "Second, don't have too many state transitions."}, {"start": 1002.0, "text": "StateReporting is designed to provide extra context"}, {"start": 1005.0, "text": "for analyzing performance over long periods of time."}, {"start": 1009.0, "text": "It is not designed for high-frequency state changes."}, {"start": 1013.0, "text": "Try to limit transitions to the cadence of user actions or slower."}, {"start": 1017.0, "text": "The system will throttle if the transition rate is too high,"}, {"start": 1021.0, "text": "and you will loose important information until rate is back under control."}, {"start": 1026.0, "text": "And lastly, confirm the correctness of your states"}, {"start": 1030.0, "text": "using tools such as the Metal Performance HUD and Instruments."}, {"start": 1034.0, "text": "Check that transitions make sense and occur when you expect."}, {"start": 1038.0, "text": "It is easy to miss edge cases that make the data wrong and hard to understand."}, {"start": 1044.0, "text": "Looking back, you now have the full picture of collecting,"}, {"start": 1048.0, "text": "analyzing, and contextualizing performance data"}, {"start": 1051.0, "text": "for long game sessions before shipping the game."}, {"start": 1054.0, "text": "But these tools don't stop at launch."}, {"start": 1058.0, "text": "Once the game lands on player devices,"}, {"start": 1060.0, "text": "its important to keep monitoring the performance,"}, {"start": 1063.0, "text": "and catch unexpected performance drops that impact player experience."}, {"start": 1068.0, "text": "Next, I will cover how you can collect key Metal performance metrics"}, {"start": 1072.0, "text": "after the game is released."}, {"start": 1074.0, "text": "MetricKit is a framework that provides two types of data:"}, {"start": 1078.0, "text": "metrics and diagnostics."}, {"start": 1081.0, "text": "It gives your game direct in-process access to power and performance data"}, {"start": 1085.0, "text": "in the form of reports."}, {"start": 1088.0, "text": "As players play your game,"}, {"start": 1090.0, "text": "MetricKit continuously collects data in the background"}, {"start": 1094.0, "text": "and delivers a daily report to your game."}, {"start": 1097.0, "text": "The report includes metrics that measures the smoothness of your game,"}, {"start": 1102.0, "text": "and how hard it's using the device resources."}, {"start": 1105.0, "text": "In macOS and iOS 27,"}, {"start": 1108.0, "text": "MetricKit exposes Metal frame rate information,"}, {"start": 1111.0, "text": "along with a ton of other interesting performance and power metrics."}, {"start": 1116.0, "text": "It also provides Metal frame rate as a function of your StateReporting states."}, {"start": 1122.0, "text": "The frame rate will be aggregated and grouped by state"}, {"start": 1125.0, "text": "in your Metric reports."}, {"start": 1127.0, "text": "In this sample report, MetricKit reports the overall average frame rate"}, {"start": 1132.0, "text": "along with time and number of frames."}, {"start": 1136.0, "text": "It also breaks down the frame rate information"}, {"start": 1138.0, "text": "by the states in the level domain."}, {"start": 1141.0, "text": "These metrics and many more will be delivered to your game process"}, {"start": 1145.0, "text": "where you can do local analysis."}, {"start": 1147.0, "text": "And also package up the data for off-device processing and aggregation."}, {"start": 1153.0, "text": "This allows you to monitor performance of your game in the field,"}, {"start": 1157.0, "text": "and identify potential issues that you can investigate and fix."}, {"start": 1162.0, "text": "In addition to metrics, MetricKit also provides diagnostics"}, {"start": 1166.0, "text": "that help you identify which code path caused a performance problem,"}, {"start": 1171.0, "text": "such as memory exceptions."}, {"start": 1173.0, "text": "When your game is terminated for exceeding its memory limit,"}, {"start": 1177.0, "text": "you get more insight on what happened."}, {"start": 1181.0, "text": "To recap, in iOS and macOS 27,"}, {"start": 1184.0, "text": "Metal performance metrics are always being recorded by the system."}, {"start": 1189.0, "text": "I demonstrated how to look-back and collect performance metrics"}, {"start": 1192.0, "text": "spanning hours and days."}, {"start": 1195.0, "text": "And how to use metalperftrace and Instruments to analyze those traces."}, {"start": 1200.0, "text": "I also showed a new StateReporting API"}, {"start": 1203.0, "text": "that can help you add context to the traces."}, {"start": 1206.0, "text": "Post shipping, MetricKit delivers metrics and diagnostics from players devices."}, {"start": 1213.0, "text": "To get started, check out StateReporting."}, {"start": 1216.0, "text": "Design the domains that matter most in your game"}, {"start": 1219.0, "text": "levels, graphics, network states, and start reporting them."}, {"start": 1224.0, "text": "Play test the game, collect long game session traces"}, {"start": 1227.0, "text": "and make sure there is a smooth player experience"}, {"start": 1230.0, "text": "on supported devices."}, {"start": 1233.0, "text": "Also remember to check out MetricKit,"}, {"start": 1235.0, "text": "and collect daily metric reports from player devices."}, {"start": 1239.0, "text": "To learn more about the available data and how to integrate MetricKit,"}, {"start": 1243.0, "text": "please check out \"Meet the new MetricKit\"."}, {"start": 1246.0, "text": "Try the new tools and workflows."}, {"start": 1249.0, "text": "And I cannot wait to play your game on Apple Platforms."}, {"start": 1252.0, "text": "Thank you for watching!"}]}