Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgraphicsframecapturemetal.mm
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <QtCore/qurl.h>
7#include "Metal/Metal.h"
8#include "qglobal.h"
9#include <QtGui/rhi/qrhi.h>
10#include <QtGui/rhi/qrhi_platform.h>
11
13
14Q_LOGGING_CATEGORY(lcGraphicsFrameCapture, "qt.gui.graphicsframecapture")
15
16#if __has_feature(objc_arc)
17#error ARC not supported
18#endif
19
20uint QGraphicsFrameCaptureMetal::frameNumber = 0;
21
22QGraphicsFrameCaptureMetal::QGraphicsFrameCaptureMetal()
23{
24 qputenv("METAL_CAPTURE_ENABLED", QByteArrayLiteral("1"));
25
26 m_captureDescriptor = [MTLCaptureDescriptor new];
27}
28
29QGraphicsFrameCaptureMetal::~QGraphicsFrameCaptureMetal()
30{
31#if defined(Q_OS_MACOS) && QT_CONFIG(process)
32 if (m_process) {
33 m_process->terminate();
34 delete m_process;
35 }
36#endif
37 [m_captureDescriptor release];
38}
39
40void QGraphicsFrameCaptureMetal::setRhi(QRhi *rhi)
41{
42 if (!rhi)
43 return;
44
45 QRhi::Implementation backend = rhi->backend();
46 const QRhiNativeHandles *nh = rhi->nativeHandles();
47
48 switch (backend) {
49 case QRhi::Implementation::Metal: {
50 const QRhiMetalNativeHandles *mtlnh = static_cast<const QRhiMetalNativeHandles *>(nh);
51 if (mtlnh->cmdQueue) {
52 m_captureDescriptor.captureObject = mtlnh->cmdQueue;
53 } else if (mtlnh->dev) {
54 m_captureDescriptor.captureObject = mtlnh->dev;
55 } else {
56 qCWarning(lcGraphicsFrameCapture) << "No valid Metal Device or Metal Command Queue found";
57 m_initialized = false;
58 return;
59 }
60 break;
61 }
62 default: {
63 qCWarning(lcGraphicsFrameCapture) << "Invalid handles were provided. MTLCaptureManager works only with Metal API";
64 m_initialized = false;
65 return;
66 break;
67 }
68 }
69
70 if (!m_captureManager) {
71 m_captureManager = MTLCaptureManager.sharedCaptureManager;
72 bool supportDocs = [m_captureManager supportsDestination:MTLCaptureDestinationGPUTraceDocument];
73 if (supportDocs) {
74 m_captureDescriptor.destination = MTLCaptureDestinationGPUTraceDocument;
75 m_initialized = true;
76 }
77 }
78}
79
80void QGraphicsFrameCaptureMetal::startCaptureFrame()
81{
82 if (!initialized()) {
83 qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. Starting capturing can not be done.";
84 return;
85 }
86
87 if (isCapturing()) {
88 qCWarning(lcGraphicsFrameCapture) << "A frame capture is already in progress,"
89 "will not initiate another one until QGraphicsFrameCapture::endCaptureFrame is called.";
90 return;
91 }
92
93 updateCaptureFileName();
94 NSError *error;
95 if (![m_captureManager startCaptureWithDescriptor:m_captureDescriptor error:&error]) {
96 QString errorMsg = QString::fromNSString(error.localizedDescription);
97 qCWarning(lcGraphicsFrameCapture, "Failed to start capture : %s", qPrintable(errorMsg));
98 }
99}
100
101void QGraphicsFrameCaptureMetal::endCaptureFrame()
102{
103 if (!initialized()) {
104 qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. End capturing can not be done.";
105 return;
106 }
107
108 if (!isCapturing()) {
109 qCWarning(lcGraphicsFrameCapture) << "A call to QGraphicsFrameCapture::endCaptureFrame can not be done"
110 " without a call to QGraphicsFrameCapture::startCaptureFrame";
111 return;
112 }
113
114 [m_captureManager stopCapture];
115 m_capturedFilesNames.append(QString::fromNSString(m_traceURL.path));
116 frameNumber++;
117}
118
119bool QGraphicsFrameCaptureMetal::initialized() const
120{
121 return m_initialized;
122}
123
124bool QGraphicsFrameCaptureMetal::isCapturing() const
125{
126 if (!initialized()) {
127 qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. Can not query if capturing is in progress or not.";
128 return false;
129 }
130
131 return [m_captureManager isCapturing];
132}
133
134void QGraphicsFrameCaptureMetal::openCapture()
135{
136#if defined(Q_OS_MACOS)
137#if !QT_CONFIG(process)
138 qFatal("QGraphicsFrameCapture requires QProcess on macOS");
139#else
140 if (!initialized()) {
141 qCWarning(lcGraphicsFrameCapture) << "Capturing on Metal was not initialized. Can not open XCode with a valid capture.";
142 return;
143 }
144
145 if (!m_process) {
146 m_process = new QProcess();
147 m_process->setProgram(QStringLiteral("xed"));
148 QStringList args;
149 args.append(QUrl::fromNSURL(m_traceURL).toLocalFile());
150 m_process->setArguments(args);
151 }
152
153 m_process->kill();
154 m_process->start();
155#endif
156#endif
157}
158
159void QGraphicsFrameCaptureMetal::updateCaptureFileName()
160{
161 m_traceURL = QUrl::fromLocalFile(m_capturePath + u"/" + m_capturePrefix + u"_"
162 + QString::number(frameNumber) + u".gputrace")
163 .toNSURL();
164 // We need to remove the trace file if it already existed else MTLCaptureManager
165 // will fail to.
166 if ([NSFileManager.defaultManager fileExistsAtPath:m_traceURL.path])
167 [NSFileManager.defaultManager removeItemAtURL:m_traceURL error:nil];
168
169 m_captureDescriptor.outputURL = m_traceURL;
170}
171
172QT_END_NAMESPACE
#define __has_feature(x)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")