Highlight
Xcode 12 adds Summary, Insights, Shader Timeline and Performance Limiter tracks to Metal Debugger and Instruments, allowing developers to find bandwidth waste, redundant passes and shader execution hotspots from a single frame capture.
Core Content
Complex Metal apps rarely have problems in just one place. A frame may contain dozens of render command encoders, thousands of draw calls, and multiple intermediate textures. The image may be displayed normally, but the GPU may still be wasting time on redundant store actions, repeated depth passes, or shader hotspots.
In the past, to troubleshoot this kind of problem, developers had to first know the architecture of Apple GPU, and then look for clues from the command buffer, encoder, counter, and texture allocation. A common dilemma for newbies is: they already know the app GPU-bound, but don’t know whether the first step is to look at memory, bandwidth, shader, or render pass dependencies.
This session puts the Xcode 12 Metal toolchain into a clearer path. Metal Debugger is responsible for single frame analysis: first capture frame, and then use Summary and Insights to find the entry. Metal System Trace is responsible for the running status over a period of time: use the timeline to observe stutter, dropped frames, memory leaks and thermal state.
Apple uses the iPad version of Larian Studios’ Divinity: Original Sin 2 as an example. This frame has 4 command buffers, 53 render command encoders, nearly 1000 draw calls, and the rendering time is about 26ms. Xcode’s Insights pointed out two actionable issues: one depth/stencil resource is not used after being stored in the store, and another depth pre-pass can be removed on Apple GPU.
Detailed Content
Summary View: Start with a single frame overview
(01:22) The entrance to Metal Debugger is frame capture. Developers first enable GPU Frame Capture in the Xcode scheme, run the Metal app, and click the camera button in the debug bar on the screen they want to analyze.
The new Summary View in Xcode 12 brings encoder overview, performance numbers, and memory usage onto the same page. Insights on the page will prompt issues affecting memory usage, bandwidth, performance or Metal API usage. Each Insight comes with a problem description, fix tips, and links to relevant documentation.
Key points:
- frame capture allows developers to obtain single-frame snapshots that can be inspected repeatedly.
- Summary View puts Overview, Performance, Memory and Insights in one place, reducing time spent searching between multiple panels.
- The value of Insights is the ability to jump from a question directly to the Dependency Viewer, Memory Viewer or other corresponding tools.
Dependency Viewer: Use dependency lines to determine bandwidth waste
(02:23) Dependency Viewer can be entered from Show Dependencies in Summary View, or from the command buffer or encoder in the navigator. It gives a bird’s eye view of all GPU passes and is suitable for checking load/store actions and bandwidth issues.
(10:37) In the sample project, Insights prompts that an encoder is storing a resource that is not used subsequently. After the speaker jumps to the Dependency Viewer, he sees that the depth texture and stencil texture are both set to Store, but there are no lines from these textures to the subsequent render command encoder. Xcode therefore recommends changing the store action toDon't Care, which can save nearly 11MB memory bandwidth.
Key points:
StoreThe attachment result will be written back after the render pass ends, and the cost is memory bandwidth.- There is no outgoing dependency, indicating that there is no encoder reading these depth/stencil results after this frame.
-
Don't CareSuitable for attachments that will no longer be used; the judgment here comes from the resource dependencies of the Dependency Viewer.
Shader Profiler and Shader Debugger: Put shader problems down to the source code line
(03:34) For shader performance issues, Xcode provides Shader Profiler. Developers switch to “View Frame by Pipeline State” and select the render pipeline, and can see the line-by-line performance breakdown of the shader source code on the Apple GPU. Profiler also supports directly editing shader sources and observing performance changes.
(04:04) For shader result errors, Xcode provides Shader Debugger. The entrance is the debug button in the debug bar. Developers can select pixels to debug fragment shaders, geometry to debug vertex shaders, and threads to debug compute shaders.
The Shader Debugger displays the status of variables across multiple threads and allows you to view values throughout a shader’s execution. Traditional CPU debuggers are prone to the problem of “step over step”; here there is no need to repeatedly step over or step into, because the variable values during execution are already retained in the debugging view once. The example given by the speaker was to look directly at the status of the 28th iteration of the loop.
Key points:
- Shader Profiler is performance-oriented and locates hot spots in shader source code lines.
- Shader Debugger expands the variable state of pixel, geometry or compute threads for correctness.
- The ability to directly edit shader sources allows quick verification of assumptions during the debugging phase.
Memory Viewer: Check resource properties and usage status
(05:24) Memory Viewer is the entrance to check memory usage in Metal Debugger. Modern rendering pipelines give developers more explicit memory control, at the cost of making resource quantities and lifecycles more difficult to manage.
Memory Viewer will list the resources and properties in the app and support filtering by volatility, resource type, access, and whether they are used. This view is suitable for answering two questions: which resources occupy memory, and which resources have no actual use in the current frame.
Key points:
- Resource breakdown helps developers view memory usage by type and access method.
- “used or not” filtering is suitable for cleaning up resources that do not participate in the current frame.
- Memory Viewer is connected to the Memory area of Summary View for jumping from overview to resource level inspection.
Metal System Trace: Putting lag back into the timeline
(06:27) Metal Debugger looks at a single frame, and Metal System Trace looks at a period of time. It is a template in Instruments and is suitable for locating time-related problems such as stutter, dropped frames, and memory leaks. Game template also provides a set of tracks suitable for game analysis.
(06:50) Encoder Timeline displays the timeline of the command buffer under GPU track and is colored by frame. Developers can check whether there is overlap between encoders, whether there is an idle bubble on the GPU, and associate GPU status with other system tracks. The example given by the speaker is thermal state: if the device is already in critical state when the encoder slows down, the app may need to reduce the workload to cool down the device.
(07:25) Shader Timeline is a new track in Xcode 12 and needs to be enabled in Metal Application recording options. It shows the shaders that were sampled during the execution of the command encoder, with the number of samples and approximate GPU time shown in the bottom table.
(07:58) Performance Limiter tracks are also enabled in Metal Application recording options under GPU Counter Set. It shows how limiter values change over time. The speaker also noted that 2020 GPU Counters will also be available for non-Apple GPUs.
(08:32) Memory Allocations track will show the allocation and release timing of buffer and texture. It is suitable for troubleshooting memory leaks and reducing the overall memory footprint.
Key points:
- Encoder Timeline is suitable to see whether the GPU is working continuously or whether there is an idle bubble.
- Shader Timeline is good for explaining why a certain encoder is taking time because it shows the sampled shader and approximate GPU time.
- Performance Limiter tracks Put limiter values on the timeline to facilitate alignment with signals such as frame and thermal state.
- Memory Allocations track pays attention to the resource life cycle and complements the time dimension that cannot be seen by the single-frame Memory Viewer.
Use Insights to find two real problems
(09:45) In the example of “Divinity: Original Sin 2”, Summary View shows that this frame has 4 command buffers, 53 render command encoders, nearly 1000 draw calls, a rendering time of about 26ms, and processes nearly 4.5 million vertices.
(10:37) The first question comes from Bandwidth Insights. A depth/stencil attachment is Stored, but the Dependency Viewer does not show subsequent use. Change store action toDon't CareFinally, nearly 11MB memory bandwidth can be saved.
(11:49) The second question comes from the Performance category. Xcode prompts that a certain render command encoder may be an unnecessary depth pass. This encoder took 1.71ms GPU time, processed about 1 million vertices, and thumbnail showed that it drew the entire scene into the depth/stencil texture. Then another encoder draws the entire scene.
The speaker judged this to be much like a traditional depth pre-pass. On non-Apple GPUs, this technique is often used to reduce overdraw. The Tile Based Deferred Rendering architecture of Apple GPU will automatically perform hidden surface removal, so this pass can be removed, saving 1.71ms GPU time.
Key points:
- Xcode does not just give the conclusion that “frame is slow”, but puts the problem into the specific encoder, resource and store action.
- Dependency Viewer uses resource dependencies to verify whether store actions are necessary.
- Thumbnail popover helps confirm if the depth pass is redrawing the entire scene.
- Apple GPU’s hidden surface removal is the basis for depth pre-pass removal.
Core Takeaways
-
Make a frame capture health check panel
- What to do: Establish a fixed process within the team, capture a frame of a key scene before each major version, and record the number of command buffers, number of encoders, number of draw calls, frame time and Insights.
- Why it’s worth doing: In the session example, Summary View exposes 26ms frame, 53 render encoders and nearly 1000 draw calls at a glance.
- How to start: Start with GPU Frame Capture in Xcode and check Overview, Performance, Memory and Insights in Summary View.
-
Make a bandwidth regression list
- What to do: Specifically check the store actions of depth, stencil, and color attachment to find resources that are not subsequently used after the Store.
- Why it’s worth doing: In the example, just changing one depth/stencil store action can save nearly 11MB of memory bandwidth.
- How to start: Jump from Insights to the corresponding encoder in Dependency Viewer, confirm whether the resource has outgoing dependency, and then change the useless Store to
Don't Care。
-
Do an Apple GPU dedicated rendering path audit
- What: Review the depth pre-pass, overdraw avoidance pass, and intermediate texture writeback strategies inherited from other GPU architectures.
- Why it’s worth doing: Apple GPU’s hidden surface removal makes some traditional depth pre-pass lose value. In the example, removing one pass can save 1.71ms GPU time.
- How to start: Use Performance Insights to find the high-cost depth pass, and then use thumbnail popover and Dependency Viewer to determine whether to redraw the entire scene.
-
Make a Caton survey template
- What to do: When the player responds to stutter or dropped frames, use Instruments’ Metal System Trace to capture a segment of the process instead of just looking at a single frame.
- Why it’s worth doing: Metal System Trace can put Encoder Timeline, thermal state, Shader Timeline, Performance Limiter and Memory Allocations on the same timeline.
- How to start: Use Game template to record the replay scene, first check if there is an idle bubble in the GPU track, and then open Shader Timeline and Performance Limiter tracks.
-
Make a shader hotspot repair loop
- What to do: Split the shader performance problem into two problems: “Which source code line is slow” and “Which variable status is wrong?”
- Why it’s worth doing: Shader Profiler gives a line-by-line performance breakdown, and Shader Debugger can view variable values across multiple threads and the entire execution process.
- How to start: In frame capture, switch to View Frame by Pipeline State, select render pipeline; for performance issues, go to Shader Profiler, and for error results, go to Shader Debugger.
Related Sessions
- Harness Apple GPUs with Metal — First understand the Tile Based Deferred Rendering architecture of Apple GPU, and then determine which traditional rendering optimizations need to be adjusted.
- Optimize Metal apps and games with GPU counters — Learn more about GPU Counter and Performance Limiter, and convert limiter signals in Instruments into optimization actions.
- Build GPU binaries with Metal — Continue to deal with shader pipeline and Pipeline State Object loading time to supplement build-time optimization in addition to run-time debugging.
- Debug GPU-side errors in Metal — Cooperate with the GPU-side error reporting of Xcode 12 to locate GPU-side errors that are difficult to cover by API validation.
- Bring your Metal app to Apple silicon Macs — Migrate the Apple GPU toolchain experience of this session to Apple silicon Macs.
Comments
GitHub Issues · utterances