Add ability to automatically capture first GPU frame.

Refactor auto GPU capture code to support both device and frame capture.
Add ability to automatically capture first GPU frame by setting
`MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE` to `2`.
Wrap GPU capture in autorelease pool to handle both cases of capture call
being made in run loop as in an app GUI, or in one-shot app like CTS.
This commit is contained in:
Bill Hollings 2021-02-04 11:56:28 -05:00
parent fcb4701f47
commit 95cf144ccf
7 changed files with 107 additions and 39 deletions

View File

@ -25,6 +25,8 @@ Released TBD
- `vkGetMoltenVKConfigurationMVK()` and `vkSetMoltenVKConfigurationMVK()` functions
can now be used with a `VkInstance` from another Vulkan layer, or with a `VK_NULL_HANDLE VkInstance`.
- `MVKConfiguration` extended to cover all MoltenVK environment variables.
- Add ability to automatically capture first GPU frame by setting `MVKConfiguration::autoGPUCaptureScope`
(or environment variable `MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE`) to `2`.
- Support _GitHub Actions_ for CI builds on pull requests.
- Remove support for _Travis-CI_.
- `Makefile` and `fetchDependencies` support `xcpretty` (if available)

View File

@ -583,6 +583,7 @@ typedef struct {
* To automatically trigger a GPU capture, set this value as follows:
* 0: No automatic GPU capture.
* 1: Capture all GPU commands issued during the lifetime of the VkDevice.
* 2: Capture all GPU commands issued during the first rendered frame.
*
* The value of this parameter must be changed before creating a VkDevice,
* for the change to take effect.

View File

@ -692,6 +692,36 @@ public:
*/
bool shouldPrefillMTLCommandBuffers();
/**
* Checks if automatic GPU capture is supported, and is enabled for the specified auto
* capture scope, and if so, starts capturing from the specified Metal capture object.
* The capture will be made either to Xcode, or to a file if one has been configured.
*
* The autoGPUCaptureScope parameter must be one of:
* - MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_NONE
* - MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE
* - MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME
*
* The mtlCaptureObject must be one of:
* - MTLDevice for scope MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE
* - MTLCommandQueue for scope MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME.
*/
void startAutoGPUCapture(int32_t autoGPUCaptureScope, id mtlCaptureObject);
/**
* Checks if automatic GPU capture is enabled for the specified
* auto capture scope, and if so, stops capturing.
*
* The autoGPUCaptureScope parameter must be one of:
* - MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_NONE
* - MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE
* - MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME
*/
void stopAutoGPUCapture(int32_t autoGPUCaptureScope);
/** Returns whether this instance is currently automatically capturing a GPU trace. */
inline bool isCurrentlyAutoGPUCapturing() { return _isCurrentlyAutoGPUCapturing; }
#pragma mark Properties directly accessible
@ -797,6 +827,7 @@ protected:
bool _useMTLEventForSemaphores;
bool _logActivityPerformanceInline;
bool _isPerformanceTracking;
bool _isCurrentlyAutoGPUCapturing;
};

View File

@ -3675,6 +3675,62 @@ bool MVKDevice::shouldPrefillMTLCommandBuffers() {
_enabledInlineUniformBlockFeatures.descriptorBindingInlineUniformBlockUpdateAfterBind));
}
void MVKDevice::startAutoGPUCapture(int32_t autoGPUCaptureScope, id mtlCaptureObject) {
if (_isCurrentlyAutoGPUCapturing || (mvkGetMVKConfiguration()->autoGPUCaptureScope != autoGPUCaptureScope)) { return; }
_isCurrentlyAutoGPUCapturing = true;
@autoreleasepool {
MTLCaptureManager *captureMgr = [MTLCaptureManager sharedCaptureManager];
// Before macOS 10.15 and iOS 13.0, captureDesc will just be nil
MTLCaptureDescriptor *captureDesc = [[MTLCaptureDescriptor new] autorelease];
captureDesc.captureObject = mtlCaptureObject;
captureDesc.destination = MTLCaptureDestinationDeveloperTools;
char* filePath = mvkGetMVKConfiguration()->autoGPUCaptureOutputFilepath;
if (strlen(filePath)) {
if ([captureMgr respondsToSelector: @selector(supportsDestination:)] &&
[captureMgr supportsDestination: MTLCaptureDestinationGPUTraceDocument] ) {
NSString* expandedFilePath = [[NSString stringWithUTF8String: filePath] stringByExpandingTildeInPath];
MVKLogInfo("Capturing GPU trace to file %s.", expandedFilePath.UTF8String);
captureDesc.destination = MTLCaptureDestinationGPUTraceDocument;
captureDesc.outputURL = [NSURL fileURLWithPath: expandedFilePath];
} else {
reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Capturing GPU traces to a file requires macOS 10.15 or iOS 13.0 and GPU capturing to be enabled. Falling back to Xcode GPU capture.");
}
} else {
MVKLogInfo("Capturing GPU trace to Xcode.");
}
// Suppress deprecation warnings for startCaptureWithXXX: on MacCatalyst.
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
if ([captureMgr respondsToSelector: @selector(startCaptureWithDescriptor:error:)] ) {
NSError *err = nil;
if ( ![captureMgr startCaptureWithDescriptor: captureDesc error: &err] ) {
reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to automatically start GPU capture session (Error code %li): %s", (long)err.code, err.localizedDescription.UTF8String);
}
} else if ([mtlCaptureObject conformsToProtocol:@protocol(MTLCommandQueue)]) {
[captureMgr startCaptureWithCommandQueue: mtlCaptureObject];
} else if ([mtlCaptureObject conformsToProtocol:@protocol(MTLDevice)]) {
[captureMgr startCaptureWithDevice: mtlCaptureObject];
}
# pragma clang diagnostic pop
}
}
void MVKDevice::stopAutoGPUCapture(int32_t autoGPUCaptureScope) {
if (_isCurrentlyAutoGPUCapturing && mvkGetMVKConfiguration()->autoGPUCaptureScope == autoGPUCaptureScope) {
[[MTLCaptureManager sharedCaptureManager] stopCapture];
_isCurrentlyAutoGPUCapturing = false;
}
}
#pragma mark Construction
@ -3695,7 +3751,8 @@ MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo
_enabledVtxAttrDivFeatures(),
_enabledPrivateDataFeatures(),
_enabledPortabilityFeatures(),
_enabledExtensions(this)
_enabledExtensions(this),
_isCurrentlyAutoGPUCapturing(false)
{
// If the physical device is lost, bail.
if (physicalDevice->getConfigurationResult() != VK_SUCCESS) {
@ -3717,41 +3774,7 @@ MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo
_commandResourceFactory = new MVKCommandResourceFactory(this);
// This code will be refactored in an upcoming release, but for now,
// suppress deprecation warnings for startCaptureWithDevice: on MacCatalyst.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (mvkGetMVKConfiguration()->autoGPUCaptureScope == MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE) {
MTLCaptureManager *captureMgr = [MTLCaptureManager sharedCaptureManager];
char* filePath = mvkGetMVKConfiguration()->autoGPUCaptureOutputFilepath;
if (strlen(filePath)) {
if ( ![captureMgr respondsToSelector: @selector(supportsDestination:)] ||
![captureMgr supportsDestination: MTLCaptureDestinationGPUTraceDocument] ) {
reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Capturing GPU traces to a file requires macOS 10.15 or iOS 13.0. Falling back to Xcode GPU capture.");
[captureMgr startCaptureWithDevice: getMTLDevice()];
} else {
NSError *err = nil;
NSString *path, *expandedPath;
MTLCaptureDescriptor *captureDesc = [MTLCaptureDescriptor new];
captureDesc.captureObject = getMTLDevice();
captureDesc.destination = MTLCaptureDestinationGPUTraceDocument;
path = [NSString stringWithUTF8String: filePath];
expandedPath = path.stringByExpandingTildeInPath;
captureDesc.outputURL = [NSURL fileURLWithPath: expandedPath];
if (![captureMgr startCaptureWithDescriptor: captureDesc error: &err]) {
reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to start GPU capture session to %s (Error code %li): %s", filePath, (long)err.code, err.localizedDescription.UTF8String);
[err release];
}
[captureDesc.outputURL release];
[captureDesc release];
[expandedPath release];
[path release];
}
} else {
[captureMgr startCaptureWithDevice: getMTLDevice()];
}
}
#pragma clang diagnostic pop
startAutoGPUCapture(MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE, getMTLDevice());
MVKLogInfo("Created VkDevice to run on GPU %s with the following %d Vulkan extensions enabled:%s",
_pProperties->deviceName,
@ -4104,9 +4127,7 @@ MVKDevice::~MVKDevice() {
[_globalVisibilityResultMTLBuffer release];
[_defaultMTLSamplerState release];
if (mvkGetMVKConfiguration()->autoGPUCaptureScope == MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE) {
[[MTLCaptureManager sharedCaptureManager] stopCapture];
}
stopAutoGPUCapture(MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE);
mvkDestroyContainerContents(_privateDataSlots);
}

View File

@ -252,6 +252,7 @@ public:
protected:
id<MTLCommandBuffer> getMTLCommandBuffer();
void stopAutoGPUCapture();
MVKSmallVector<MVKPresentTimingInfo, 4> _presentInfo;
};

View File

@ -199,6 +199,8 @@ void MVKQueue::initGPUCaptureScopes() {
_submissionCaptureScope->makeDefault();
}
_submissionCaptureScope->beginScope(); // Allow Xcode to capture the first frame if desired.
getDevice()->startAutoGPUCapture(MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME, _mtlQueue);
}
MVKQueue::~MVKQueue() {
@ -398,6 +400,7 @@ void MVKQueuePresentSurfaceSubmission::execute() {
auto cs = _queue->_submissionCaptureScope;
cs->endScope();
cs->beginScope();
stopAutoGPUCapture();
this->destroy();
}
@ -409,6 +412,14 @@ id<MTLCommandBuffer> MVKQueuePresentSurfaceSubmission::getMTLCommandBuffer() {
return mtlCmdBuff;
}
void MVKQueuePresentSurfaceSubmission::stopAutoGPUCapture() {
const MVKConfiguration* pMVKConfig = mvkGetMVKConfiguration();
if (_queue->_queueFamily->getIndex() == pMVKConfig->defaultGPUCaptureScopeQueueFamilyIndex &&
_queue->_index == pMVKConfig->defaultGPUCaptureScopeQueueIndex) {
_queue->getDevice()->stopAutoGPUCapture(MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME);
}
}
MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* queue,
const VkPresentInfoKHR* pPresentInfo)
: MVKQueueSubmission(queue, pPresentInfo->waitSemaphoreCount, pPresentInfo->pWaitSemaphores) {

View File

@ -224,6 +224,7 @@ void mvkSetMVKConfiguration(MVKConfiguration* pMVKConfig);
*/
#define MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_NONE 0
#define MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE 1
#define MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME 2
#ifndef MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE
# define MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_NONE
#endif