Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgraphicsframecapturerenderdoc.cpp
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
5#include <QtCore/qcoreapplication.h>
6#include <QtCore/qfile.h>
7#include <QtCore/qlibrary.h>
8#include <QtCore/qmutex.h>
9#include "QtGui/rhi/qrhi.h"
10#include "QtGui/rhi/qrhi_platform.h"
11#include "QtGui/qopenglcontext.h"
12
14
15Q_LOGGING_CATEGORY(lcGraphicsFrameCapture, "qt.gui.graphicsframecapture")
16
17RENDERDOC_API_1_6_0 *QGraphicsFrameCaptureRenderDoc::s_rdocApi = nullptr;
18#if QT_CONFIG(thread)
19QBasicMutex QGraphicsFrameCaptureRenderDoc::s_frameCaptureMutex;
20#endif
21
22#if QT_CONFIG(opengl)
23static void *glNativeContext(QOpenGLContext *context) {
24 void *nctx = nullptr;
25 if (context != nullptr && context->isValid()) {
26#ifdef Q_OS_WIN
27 nctx = context->nativeInterface<QNativeInterface::QWGLContext>()->nativeContext();
28#endif
29
30#ifdef Q_OS_LINUX
31#if QT_CONFIG(egl)
32 QNativeInterface::QEGLContext *eglItf = context->nativeInterface<QNativeInterface::QEGLContext>();
33 if (eglItf)
34 nctx = eglItf->nativeContext();
35#endif
36
37#if QT_CONFIG(xcb_glx_plugin)
38 QNativeInterface::QGLXContext *glxItf = context->nativeInterface<QNativeInterface::QGLXContext>();
39 if (glxItf)
40 nctx = glxItf->nativeContext();
41#endif
42#endif
43
44#if QT_CONFIG(metal)
45 nctx = context->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext();
46#endif
47 }
48 return nctx;
49}
50#endif // QT_CONFIG(opengl)
51
97 : m_nativeHandlesSet(false)
98{
99 if (!s_rdocApi)
100 init();
101}
102
104{
105 if (!rhi)
106 return;
107
108 QRhi::Implementation backend = rhi->backend();
109 const QRhiNativeHandles *nh = rhi->nativeHandles();
110
111 switch (backend) {
113#ifdef Q_OS_WIN
114 const QRhiD3D11NativeHandles *d3d11nh = static_cast<const QRhiD3D11NativeHandles *>(nh);
115 m_nativeHandle = d3d11nh->dev;
116 break;
117#endif
118 qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for D3D11. Check platform support";
119 break;
120 }
122#ifdef Q_OS_WIN
123 const QRhiD3D12NativeHandles *d3d12nh = static_cast<const QRhiD3D12NativeHandles *>(nh);
124 m_nativeHandle = d3d12nh->dev;
125 break;
126#endif
127 qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for D3D12. Check platform support";
128 break;
129 }
131#if QT_CONFIG(vulkan)
132 const QRhiVulkanNativeHandles *vknh = static_cast<const QRhiVulkanNativeHandles *>(nh);
133 m_nativeHandle = RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(vknh->inst->vkInstance());
134 break;
135#endif
136 qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for Vulkan. Check platform support";
137 break;
138 }
140#ifndef QT_NO_OPENGL
141 const QRhiGles2NativeHandles *glnh = static_cast<const QRhiGles2NativeHandles *>(nh);
142 m_nativeHandle = glNativeContext(glnh->context);
143 if (m_nativeHandle)
144 break;
145#endif
146 qCWarning(lcGraphicsFrameCapture) << "Could not find valid handles for OpenGL. Check platform support";
147 break;
148 }
151 qCWarning(lcGraphicsFrameCapture) << "Invalid handles were provided."
152 " Metal and Null backends are not supported with RenderDoc";
153 break;
154 }
155
156 if (m_nativeHandle)
157 m_nativeHandlesSet = true;
158}
159
166{
167
168 if (!initialized()) {
169 qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
170 " Starting capturing can not be done.";
171 return;
172 }
173
174#if QT_CONFIG(thread)
175 // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
176 QMutexLocker locker(&s_frameCaptureMutex);
177#endif
178 if (s_rdocApi->IsFrameCapturing()) {
179 qCWarning(lcGraphicsFrameCapture) << "A frame capture is already in progress, "
180 "will not initiate another one until"
181 " QGraphicsFrameCapture::endCaptureFrame is called.";
182 return;
183 }
184
185 qCInfo(lcGraphicsFrameCapture) << "A frame capture is going to start.";
186 updateCapturePathAndTemplate();
187 s_rdocApi->StartFrameCapture(m_nativeHandle, nullptr);
188}
189
198{
199 if (!initialized()) {
200 qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
201 " End capturing can not be done.";
202 return;
203 }
204
205#if QT_CONFIG(thread)
206 // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
207 QMutexLocker locker(&s_frameCaptureMutex);
208#endif
209 if (!s_rdocApi->IsFrameCapturing()) {
210 qCWarning(lcGraphicsFrameCapture) << "A call to QGraphicsFrameCapture::endCaptureFrame can not be done"
211 " without a call to QGraphicsFrameCapture::startCaptureFrame";
212 return;
213 }
214
215 qCInfo(lcGraphicsFrameCapture) << "A frame capture is going to end.";
216 uint32_t result = s_rdocApi->EndFrameCapture(m_nativeHandle, nullptr);
217
218 if (result) {
219 uint32_t count = s_rdocApi->GetNumCaptures();
220 uint32_t pathLength = 0;
221 s_rdocApi->GetCapture(count - 1, nullptr, &pathLength, nullptr);
222 if (pathLength > 0) {
223 QVarLengthArray<char> name(pathLength, 0);
224 s_rdocApi->GetCapture(count - 1, name.data(), &pathLength, nullptr);
225 m_capturedFilesNames.append(QString::fromUtf8(name.data(), -1));
226 }
227 }
228}
229
230void QGraphicsFrameCaptureRenderDoc::updateCapturePathAndTemplate()
231{
232 if (!initialized()) {
233 qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
234 " Updating save location can not be done.";
235 return;
236 }
237
238
239 QString rdocFilePathTemplate = m_capturePath + QStringLiteral("/") + m_capturePrefix;
240 s_rdocApi->SetCaptureFilePathTemplate(rdocFilePathTemplate.toUtf8().constData());
241}
242
247{
248 return s_rdocApi && m_nativeHandlesSet;
249}
250
252{
253 if (!initialized()) {
254 qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
255 " Can not query if capturing is in progress or not.";
256 return false;
257 }
258
259 return s_rdocApi->IsFrameCapturing();
260}
261
263{
264 if (!initialized()) {
265 qCWarning(lcGraphicsFrameCapture) << "RenderDoc was not initialized."
266 " Can not open RenderDoc UI tool.";
267 return;
268 }
269
270#if QT_CONFIG(thread)
271 // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
272 QMutexLocker locker(&s_frameCaptureMutex);
273#endif
274 if (s_rdocApi->IsTargetControlConnected())
275 s_rdocApi->ShowReplayUI();
276 else
277 s_rdocApi->LaunchReplayUI(1, nullptr);
278}
279
280void QGraphicsFrameCaptureRenderDoc::init()
281{
282#if QT_CONFIG(thread)
283 // There is a single instance of RenderDoc library and it needs mutex for multithreading access.
284 QMutexLocker locker(&s_frameCaptureMutex);
285#endif
286
287 QLibrary renderDocLib(QStringLiteral("renderdoc"));
288 pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI) renderDocLib.resolve("RENDERDOC_GetAPI");
289 if (!renderDocLib.isLoaded() || (RENDERDOC_GetAPI == nullptr)) {
290 qCWarning(lcGraphicsFrameCapture) << renderDocLib.errorString().toLatin1();
291 return;
292 }
293
294 int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, static_cast<void **>(static_cast<void *>(&s_rdocApi)));
295
296 if (ret == 0) {
297 qCWarning(lcGraphicsFrameCapture) << "The requested RenderDoc API is invalid or not supported";
298 return;
299 }
300
301 s_rdocApi->MaskOverlayBits(RENDERDOC_OverlayBits::eRENDERDOC_Overlay_None,
302 RENDERDOC_OverlayBits::eRENDERDOC_Overlay_None);
303 s_rdocApi->SetCaptureKeys(nullptr, 0);
304 s_rdocApi->SetFocusToggleKeys(nullptr, 0);
305
306 QString rdocFilePathTemplate = m_capturePath + QStringLiteral("/") + m_capturePrefix;
307 s_rdocApi->SetCaptureFilePathTemplate(rdocFilePathTemplate.toUtf8().constData());
308}
309
The QGraphicsFrameCaptureRenderDoc class provides a way to capture a record of draw calls for differe...
bool initialized() const override
Returns true if the API is loaded and can capture frames or not.
QGraphicsFrameCaptureRenderDoc()
Creates a new object of this class.
void startCaptureFrame() override
Starts a frame capture using the set native handles provided through QGraphicsFrameCaptureRenderDoc::...
void endCaptureFrame() override
Ends a frame capture started by a call to QGraphicsFrameCaptureRenderDoc::startCaptureFrame using the...
\inmodule QtCore \reentrant
Definition qlibrary.h:17
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
Native interface to an NSOpenGLContext on \macos.
\inheaderfile QOpenGLContext
\inmodule QtGui
\variable QRhiD3D11InitParams::enableDebugLayer
\variable QRhiGles2InitParams::format
\variable QRhiVulkanInitParams::inst
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
Implementation backend() const
Definition qrhi.cpp:8651
Implementation
Describes which graphics API-specific backend gets used by a QRhi instance.
Definition qrhi.h:1806
@ Metal
Definition qrhi.h:1811
@ Vulkan
Definition qrhi.h:1808
@ Null
Definition qrhi.h:1807
@ D3D11
Definition qrhi.h:1810
@ D3D12
Definition qrhi.h:1812
@ OpenGLES2
Definition qrhi.h:1809
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10137
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
Combined button and popup list for selecting options.
static void * context
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
return ret
GLenum GLenum GLsizei count
GLuint name
GLuint64EXT * result
[6]
#define QStringLiteral(str)
\variable QRhiReadbackResult::completed
Definition qrhi.h:800