'MacOS Metal: Failing to capture GPU Frame from command line app

I'm trying to programmatically capture GPU frames using MTLCaptureManager in a command line application.

So far, the capture manager fails to support the MTLCaptureDestinationGPUTraceDocument destination.

I tried to create a very minimal repro case using XCode :

#import <Foundation/Foundation.h>
#import <Metal/Metal.h>

int main(int argc, const char * argv[]) {
  @autoreleasepool {

      id<MTLDevice> device = MTLCreateSystemDefaultDevice();
      
      MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager];
      if (![captureManager supportsDestination:MTLCaptureDestinationGPUTraceDocument])
      {
        NSLog(@"********** captureManager does not support MTLCaptureDestinationGPUTraceDocument ************");
      }
      else
      {
        NSLog(@"captureManager support is fine");
      }
  }
  return 0;
}

When run with XCode, it seems to be willing to work : the output is :

2020-09-02 16:25:59.712217+0200 testMetalCapture[20095:416447] Metal GPU Frame Capture Enabled
2020-09-02 16:25:59.712503+0200 testMetalCapture[20095:416447] Metal API Validation Enabled
2020-09-02 16:26:00.669092+0200 testMetalCapture[20095:416447] captureManager support is fine
Program ended with exit code: 0

But when I archive the build result, and run from a terminal, it fails :

2020-09-02 16:32:57.607 testMetalCapture[20126:419837] ********** captureManager does not support MTLCaptureDestinationGPUTraceDocument ************

Is there any runtime environment I could reproduce in terminal to get the MTLCaptureManager working ?

(Environment is XCode 11.6 + MacOS 10.15 Catalina)



Solution 1:[1]

From what I understood (I could not find any official documentation) :

  • MTLCaptureManager needs an authorization from the Info.plist: the MetalCaptureEnabled should set to YES.
  • The proper way is to bundle the command line application with such a plist

I found by accident that MTLCaptureManager also works if there's an Info.plist in the same directory as the command line application.

enter image description here

This Info.plist can be almost empty, like this :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>MetalCaptureEnabled</key>
    <true/>
</dict>
</plist>

With this simple setup, I can run my test program (and my real one too)

% ./testMetalCapture
2020-10-02 15:53:08.507 testMetalCapture[28559:686864] Metal GPU Frame Capture Enabled
2020-10-02 15:53:08.523 testMetalCapture[28559:686864] captureManager support is fine

(I'm currently using MacOSX 10.15.6, it may break in the future)

Solution 2:[2]

The trick from @rotoglup was working for me, until it wasn't anymore: some buffers were full of zeros, and no image appeared in the 'screenshots' inside the metal debugger. I don't know if this was because of an update of the OS or of XCode or something else.

I still needed an Info.plist with this content, as @rotoglup suggested:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>MetalCaptureEnabled</key>
    <true/>
</dict>
</plist>

but I also needed to set the METAL_DEVICE_WRAPPER_TYPE env variable to 1 in the terminal that launches the command line app:

export METAL_DEVICE_WRAPPER_TYPE=1

With this, buffers are filled up correctly, and I get non-empty screenshots.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 rotoglup
Solution 2 stefanovic