1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQuick module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qsgrhisupport_p.h" |
41 | #include "qsgcontext_p.h" |
42 | #if QT_CONFIG(opengl) |
43 | # include "qsgdefaultrendercontext_p.h" |
44 | #endif |
45 | #include <QtGui/qwindow.h> |
46 | |
47 | #if QT_CONFIG(vulkan) |
48 | #include <QtGui/qvulkaninstance.h> |
49 | #endif |
50 | |
51 | QT_BEGIN_NAMESPACE |
52 | |
53 | #if QT_CONFIG(vulkan) |
54 | QVulkanInstance *s_vulkanInstance = nullptr; |
55 | #endif |
56 | |
57 | QVulkanInstance *QSGRhiSupport::vulkanInstance() |
58 | { |
59 | #if QT_CONFIG(vulkan) |
60 | QSGRhiSupport *inst = QSGRhiSupport::instance(); |
61 | if (!inst->isRhiEnabled() || inst->rhiBackend() != QRhi::Vulkan) |
62 | return nullptr; |
63 | |
64 | if (!s_vulkanInstance) { |
65 | s_vulkanInstance = new QVulkanInstance; |
66 | if (inst->isDebugLayerRequested()) { |
67 | #ifndef Q_OS_ANDROID |
68 | s_vulkanInstance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation" ); |
69 | #else |
70 | s_vulkanInstance->setLayers(QByteArrayList() |
71 | << "VK_LAYER_GOOGLE_threading" |
72 | << "VK_LAYER_LUNARG_parameter_validation" |
73 | << "VK_LAYER_LUNARG_object_tracker" |
74 | << "VK_LAYER_LUNARG_core_validation" |
75 | << "VK_LAYER_LUNARG_image" |
76 | << "VK_LAYER_LUNARG_swapchain" |
77 | << "VK_LAYER_GOOGLE_unique_objects" ); |
78 | #endif |
79 | } |
80 | s_vulkanInstance->setExtensions(QByteArrayList() |
81 | << "VK_KHR_get_physical_device_properties2" ); |
82 | if (!s_vulkanInstance->create()) { |
83 | qWarning(msg: "Failed to create Vulkan instance" ); |
84 | delete s_vulkanInstance; |
85 | s_vulkanInstance = nullptr; |
86 | } |
87 | } |
88 | return s_vulkanInstance; |
89 | #else |
90 | return nullptr; |
91 | #endif |
92 | } |
93 | |
94 | void QSGRhiSupport::cleanup() |
95 | { |
96 | #if QT_CONFIG(vulkan) |
97 | delete s_vulkanInstance; |
98 | s_vulkanInstance = nullptr; |
99 | #endif |
100 | } |
101 | |
102 | QSGRhiSupport::QSGRhiSupport() |
103 | : m_set(false), |
104 | m_enableRhi(false), |
105 | m_debugLayer(false), |
106 | m_profile(false), |
107 | m_shaderEffectDebug(false), |
108 | m_preferSoftwareRenderer(false) |
109 | { |
110 | } |
111 | |
112 | void QSGRhiSupport::applySettings() |
113 | { |
114 | m_set = true; |
115 | |
116 | // This is also done when creating the renderloop but we may be before that |
117 | // in case we get here due to a setScenegraphBackend() -> configure() early |
118 | // on in main(). Avoid losing info logs since troubleshooting gets |
119 | // confusing otherwise. |
120 | QSGRhiSupport::checkEnvQSgInfo(); |
121 | |
122 | if (m_requested.valid) { |
123 | // explicit rhi backend request from C++ (e.g. via QQuickWindow) |
124 | m_enableRhi = m_requested.rhi; |
125 | switch (m_requested.api) { |
126 | case QSGRendererInterface::OpenGLRhi: |
127 | m_rhiBackend = QRhi::OpenGLES2; |
128 | break; |
129 | case QSGRendererInterface::Direct3D11Rhi: |
130 | m_rhiBackend = QRhi::D3D11; |
131 | break; |
132 | case QSGRendererInterface::VulkanRhi: |
133 | m_rhiBackend = QRhi::Vulkan; |
134 | break; |
135 | case QSGRendererInterface::MetalRhi: |
136 | m_rhiBackend = QRhi::Metal; |
137 | break; |
138 | case QSGRendererInterface::NullRhi: |
139 | m_rhiBackend = QRhi::Null; |
140 | break; |
141 | default: |
142 | Q_ASSERT_X(false, "QSGRhiSupport" , "Internal error: unhandled GraphicsApi type" ); |
143 | break; |
144 | } |
145 | } else { |
146 | // check env.vars., fall back to platform-specific defaults when backend is not set |
147 | m_enableRhi = uint(qEnvironmentVariableIntValue(varName: "QSG_RHI" )); |
148 | const QByteArray rhiBackend = qgetenv(varName: "QSG_RHI_BACKEND" ); |
149 | if (rhiBackend == QByteArrayLiteral("gl" ) |
150 | || rhiBackend == QByteArrayLiteral("gles2" ) |
151 | || rhiBackend == QByteArrayLiteral("opengl" )) |
152 | { |
153 | m_rhiBackend = QRhi::OpenGLES2; |
154 | } else if (rhiBackend == QByteArrayLiteral("d3d11" ) || rhiBackend == QByteArrayLiteral("d3d" )) { |
155 | m_rhiBackend = QRhi::D3D11; |
156 | } else if (rhiBackend == QByteArrayLiteral("vulkan" )) { |
157 | m_rhiBackend = QRhi::Vulkan; |
158 | } else if (rhiBackend == QByteArrayLiteral("metal" )) { |
159 | m_rhiBackend = QRhi::Metal; |
160 | } else if (rhiBackend == QByteArrayLiteral("null" )) { |
161 | m_rhiBackend = QRhi::Null; |
162 | } else { |
163 | if (!rhiBackend.isEmpty()) { |
164 | qWarning(msg: "Unknown key \"%s\" for QSG_RHI_BACKEND, falling back to default backend." , |
165 | rhiBackend.constData()); |
166 | } |
167 | #if defined(Q_OS_WIN) |
168 | m_rhiBackend = QRhi::D3D11; |
169 | #elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) |
170 | m_rhiBackend = QRhi::Metal; |
171 | #else |
172 | m_rhiBackend = QRhi::OpenGLES2; |
173 | #endif |
174 | // Vulkan has to be requested explicitly |
175 | } |
176 | } |
177 | |
178 | if (!m_enableRhi) |
179 | return; |
180 | |
181 | // validation layers (Vulkan) or debug layer (D3D) |
182 | m_debugLayer = uint(qEnvironmentVariableIntValue(varName: "QSG_RHI_DEBUG_LAYER" )); |
183 | |
184 | // EnableProfiling + DebugMarkers |
185 | m_profile = uint(qEnvironmentVariableIntValue(varName: "QSG_RHI_PROFILE" )); |
186 | |
187 | m_shaderEffectDebug = uint(qEnvironmentVariableIntValue(varName: "QSG_RHI_SHADEREFFECT_DEBUG" )); |
188 | |
189 | m_preferSoftwareRenderer = uint(qEnvironmentVariableIntValue(varName: "QSG_RHI_PREFER_SOFTWARE_RENDERER" )); |
190 | |
191 | m_killDeviceFrameCount = qEnvironmentVariableIntValue(varName: "QSG_RHI_SIMULATE_DEVICE_LOSS" ); |
192 | if (m_killDeviceFrameCount > 0 && m_rhiBackend == QRhi::D3D11) |
193 | qDebug(msg: "Graphics device will be reset every %d frames" , m_killDeviceFrameCount); |
194 | |
195 | const QString backendName = rhiBackendName(); |
196 | qCDebug(QSG_LOG_INFO, |
197 | "Using QRhi with backend %s\n graphics API debug/validation layers: %d\n QRhi profiling and debug markers: %d" , |
198 | qPrintable(backendName), m_debugLayer, m_profile); |
199 | if (m_preferSoftwareRenderer) |
200 | qCDebug(QSG_LOG_INFO, "Prioritizing software renderers" ); |
201 | } |
202 | |
203 | QSGRhiSupport *QSGRhiSupport::staticInst() |
204 | { |
205 | static QSGRhiSupport inst; |
206 | return &inst; |
207 | } |
208 | |
209 | void QSGRhiSupport::checkEnvQSgInfo() |
210 | { |
211 | // For compatibility with 5.3 and earlier's QSG_INFO environment variables |
212 | if (qEnvironmentVariableIsSet(varName: "QSG_INFO" )) |
213 | const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(type: QtDebugMsg, enable: true); |
214 | } |
215 | |
216 | void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api) |
217 | { |
218 | Q_ASSERT(QSGRendererInterface::isApiRhiBased(api)); |
219 | QSGRhiSupport *inst = staticInst(); |
220 | if (inst->m_set) { |
221 | qWarning(msg: "QRhi is already configured, request ignored" ); |
222 | return; |
223 | } |
224 | inst->m_requested.valid = true; |
225 | inst->m_requested.api = api; |
226 | inst->m_requested.rhi = true; |
227 | inst->applySettings(); |
228 | } |
229 | |
230 | QSGRhiSupport *QSGRhiSupport::instance() |
231 | { |
232 | QSGRhiSupport *inst = staticInst(); |
233 | if (!inst->m_set) |
234 | inst->applySettings(); |
235 | return inst; |
236 | } |
237 | |
238 | QString QSGRhiSupport::rhiBackendName() const |
239 | { |
240 | if (m_enableRhi) { |
241 | switch (m_rhiBackend) { |
242 | case QRhi::Null: |
243 | return QLatin1String("Null" ); |
244 | case QRhi::Vulkan: |
245 | return QLatin1String("Vulkan" ); |
246 | case QRhi::OpenGLES2: |
247 | return QLatin1String("OpenGL" ); |
248 | case QRhi::D3D11: |
249 | return QLatin1String("D3D11" ); |
250 | case QRhi::Metal: |
251 | return QLatin1String("Metal" ); |
252 | default: |
253 | return QLatin1String("Unknown" ); |
254 | } |
255 | } |
256 | return QLatin1String("Unknown (RHI not enabled)" ); |
257 | } |
258 | |
259 | QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const |
260 | { |
261 | if (!m_enableRhi) |
262 | return QSGRendererInterface::OpenGL; |
263 | |
264 | switch (m_rhiBackend) { |
265 | case QRhi::Null: |
266 | return QSGRendererInterface::NullRhi; |
267 | case QRhi::Vulkan: |
268 | return QSGRendererInterface::VulkanRhi; |
269 | case QRhi::OpenGLES2: |
270 | return QSGRendererInterface::OpenGLRhi; |
271 | case QRhi::D3D11: |
272 | return QSGRendererInterface::Direct3D11Rhi; |
273 | case QRhi::Metal: |
274 | return QSGRendererInterface::MetalRhi; |
275 | default: |
276 | return QSGRendererInterface::Unknown; |
277 | } |
278 | } |
279 | |
280 | QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const |
281 | { |
282 | if (!m_enableRhi) |
283 | return QSurface::OpenGLSurface; |
284 | |
285 | switch (m_rhiBackend) { |
286 | case QRhi::Vulkan: |
287 | return QSurface::VulkanSurface; |
288 | case QRhi::OpenGLES2: |
289 | return QSurface::OpenGLSurface; |
290 | case QRhi::D3D11: |
291 | return QSurface::OpenGLSurface; // yup, OpenGLSurface |
292 | case QRhi::Metal: |
293 | return QSurface::MetalSurface; |
294 | default: |
295 | return QSurface::OpenGLSurface; |
296 | } |
297 | } |
298 | |
299 | #if QT_CONFIG(vulkan) |
300 | static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, |
301 | const QRhiNativeHandles *nat, |
302 | const QRhiNativeHandles *cbNat, |
303 | const QRhiNativeHandles *rpNat) |
304 | { |
305 | const QRhiVulkanNativeHandles *vknat = static_cast<const QRhiVulkanNativeHandles *>(nat); |
306 | const QRhiVulkanCommandBufferNativeHandles *maybeVkCbNat = |
307 | static_cast<const QRhiVulkanCommandBufferNativeHandles *>(cbNat); |
308 | const QRhiVulkanRenderPassNativeHandles *maybeVkRpNat = |
309 | static_cast<const QRhiVulkanRenderPassNativeHandles *>(rpNat); |
310 | |
311 | switch (res) { |
312 | case QSGRendererInterface::DeviceResource: |
313 | return &vknat->dev; |
314 | case QSGRendererInterface::CommandQueueResource: |
315 | return &vknat->gfxQueue; |
316 | case QSGRendererInterface::CommandListResource: |
317 | if (maybeVkCbNat) |
318 | return &maybeVkCbNat->commandBuffer; |
319 | else |
320 | return nullptr; |
321 | case QSGRendererInterface::PhysicalDeviceResource: |
322 | return &vknat->physDev; |
323 | case QSGRendererInterface::RenderPassResource: |
324 | if (maybeVkRpNat) |
325 | return &maybeVkRpNat->renderPass; |
326 | else |
327 | return nullptr; |
328 | default: |
329 | return nullptr; |
330 | } |
331 | } |
332 | #endif |
333 | |
334 | #if QT_CONFIG(opengl) |
335 | static const void *qsgrhi_gl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat) |
336 | { |
337 | const QRhiGles2NativeHandles *glnat = static_cast<const QRhiGles2NativeHandles *>(nat); |
338 | switch (res) { |
339 | case QSGRendererInterface::OpenGLContextResource: |
340 | return glnat->context; |
341 | default: |
342 | return nullptr; |
343 | } |
344 | } |
345 | #endif |
346 | |
347 | #ifdef Q_OS_WIN |
348 | static const void *qsgrhi_d3d11_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat) |
349 | { |
350 | const QRhiD3D11NativeHandles *d3dnat = static_cast<const QRhiD3D11NativeHandles *>(nat); |
351 | switch (res) { |
352 | case QSGRendererInterface::DeviceResource: |
353 | return d3dnat->dev; |
354 | case QSGRendererInterface::DeviceContextResource: |
355 | return d3dnat->context; |
356 | default: |
357 | return nullptr; |
358 | } |
359 | } |
360 | #endif |
361 | |
362 | #if defined(Q_OS_MACOS) || defined(Q_OS_IOS) |
363 | static const void *qsgrhi_mtl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat, |
364 | const QRhiNativeHandles *cbNat) |
365 | { |
366 | const QRhiMetalNativeHandles *mtlnat = static_cast<const QRhiMetalNativeHandles *>(nat); |
367 | const QRhiMetalCommandBufferNativeHandles *maybeMtlCbNat = |
368 | static_cast<const QRhiMetalCommandBufferNativeHandles *>(cbNat); |
369 | |
370 | switch (res) { |
371 | case QSGRendererInterface::DeviceResource: |
372 | return mtlnat->dev; |
373 | case QSGRendererInterface::CommandQueueResource: |
374 | return mtlnat->cmdQueue; |
375 | case QSGRendererInterface::CommandListResource: |
376 | if (maybeMtlCbNat) |
377 | return maybeMtlCbNat->commandBuffer; |
378 | else |
379 | return nullptr; |
380 | case QSGRendererInterface::CommandEncoderResource: |
381 | if (maybeMtlCbNat) |
382 | return maybeMtlCbNat->encoder; |
383 | else |
384 | return nullptr; |
385 | default: |
386 | return nullptr; |
387 | } |
388 | } |
389 | #endif |
390 | |
391 | const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res, |
392 | const QSGDefaultRenderContext *rc) |
393 | { |
394 | // ### This condition is a temporary workaround to allow compilation |
395 | // with -no-opengl, but Vulkan or Metal enabled, to succeed. Full |
396 | // support for RHI-capable -no-opengl builds will be available in |
397 | // Qt 6 once the direct OpenGL code path gets removed. |
398 | #if QT_CONFIG(opengl) |
399 | |
400 | QRhi *rhi = rc->rhi(); |
401 | if (res == QSGRendererInterface::RhiResource || !rhi) |
402 | return rhi; |
403 | |
404 | const QRhiNativeHandles *nat = rhi->nativeHandles(); |
405 | if (!nat) |
406 | return nullptr; |
407 | |
408 | switch (m_rhiBackend) { |
409 | #if QT_CONFIG(vulkan) |
410 | case QRhi::Vulkan: |
411 | { |
412 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
413 | QRhiRenderPassDescriptor *rp = rc->currentFrameRenderPass(); |
414 | return qsgrhi_vk_rifResource(res, nat, |
415 | cbNat: cb ? cb->nativeHandles() : nullptr, |
416 | rpNat: rp ? rp->nativeHandles() : nullptr); |
417 | } |
418 | #endif |
419 | #if QT_CONFIG(opengl) |
420 | case QRhi::OpenGLES2: |
421 | return qsgrhi_gl_rifResource(res, nat); |
422 | #endif |
423 | #ifdef Q_OS_WIN |
424 | case QRhi::D3D11: |
425 | return qsgrhi_d3d11_rifResource(res, nat); |
426 | #endif |
427 | #if defined(Q_OS_MACOS) || defined(Q_OS_IOS) |
428 | case QRhi::Metal: |
429 | { |
430 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
431 | return qsgrhi_mtl_rifResource(res, nat, cb ? cb->nativeHandles() : nullptr); |
432 | } |
433 | #endif |
434 | default: |
435 | return nullptr; |
436 | } |
437 | |
438 | #else |
439 | Q_UNUSED(res); |
440 | Q_UNUSED(rc); |
441 | return nullptr; |
442 | #endif |
443 | } |
444 | |
445 | int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi) |
446 | { |
447 | int msaaSampleCount = qMax(a: QSurfaceFormat::defaultFormat().samples(), b: window->requestedFormat().samples()); |
448 | if (qEnvironmentVariableIsSet(varName: "QSG_SAMPLES" )) |
449 | msaaSampleCount = qEnvironmentVariableIntValue(varName: "QSG_SAMPLES" ); |
450 | msaaSampleCount = qMax(a: 1, b: msaaSampleCount); |
451 | if (msaaSampleCount > 1) { |
452 | const QVector<int> supportedSampleCounts = rhi->supportedSampleCounts(); |
453 | if (!supportedSampleCounts.contains(t: msaaSampleCount)) { |
454 | int reducedSampleCount = 1; |
455 | for (int i = supportedSampleCounts.count() - 1; i >= 0; --i) { |
456 | if (supportedSampleCounts[i] <= msaaSampleCount) { |
457 | reducedSampleCount = supportedSampleCounts[i]; |
458 | break; |
459 | } |
460 | } |
461 | qWarning() << "Requested MSAA sample count" << msaaSampleCount |
462 | << "but supported sample counts are" << supportedSampleCounts |
463 | << ", using sample count" << reducedSampleCount << "instead" ; |
464 | msaaSampleCount = reducedSampleCount; |
465 | } |
466 | } |
467 | return msaaSampleCount; |
468 | } |
469 | |
470 | // must be called on the main thread |
471 | QOffscreenSurface *QSGRhiSupport::maybeCreateOffscreenSurface(QWindow *window) |
472 | { |
473 | QOffscreenSurface *offscreenSurface = nullptr; |
474 | #if QT_CONFIG(opengl) |
475 | if (rhiBackend() == QRhi::OpenGLES2) { |
476 | const QSurfaceFormat format = window->requestedFormat(); |
477 | offscreenSurface = QRhiGles2InitParams::newFallbackSurface(format); |
478 | } |
479 | #else |
480 | Q_UNUSED(window); |
481 | #endif |
482 | return offscreenSurface; |
483 | } |
484 | |
485 | // must be called on the render thread |
486 | QRhi *QSGRhiSupport::createRhi(QWindow *window, QOffscreenSurface *offscreenSurface) |
487 | { |
488 | #if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) |
489 | Q_UNUSED(window); |
490 | #endif |
491 | |
492 | QRhi *rhi = nullptr; |
493 | |
494 | QRhi::Flags flags; |
495 | if (isProfilingRequested()) |
496 | flags |= QRhi::EnableProfiling | QRhi::EnableDebugMarkers; |
497 | if (isSoftwareRendererRequested()) |
498 | flags |= QRhi::PreferSoftwareRenderer; |
499 | |
500 | QRhi::Implementation backend = rhiBackend(); |
501 | if (backend == QRhi::Null) { |
502 | QRhiNullInitParams rhiParams; |
503 | rhi = QRhi::create(impl: backend, params: &rhiParams, flags); |
504 | } |
505 | #if QT_CONFIG(opengl) |
506 | if (backend == QRhi::OpenGLES2) { |
507 | const QSurfaceFormat format = window->requestedFormat(); |
508 | QRhiGles2InitParams rhiParams; |
509 | rhiParams.format = format; |
510 | rhiParams.fallbackSurface = offscreenSurface; |
511 | rhiParams.window = window; |
512 | rhi = QRhi::create(impl: backend, params: &rhiParams, flags); |
513 | } |
514 | #else |
515 | Q_UNUSED(offscreenSurface); |
516 | #endif |
517 | #if QT_CONFIG(vulkan) |
518 | if (backend == QRhi::Vulkan) { |
519 | QRhiVulkanInitParams rhiParams; |
520 | rhiParams.inst = window->vulkanInstance(); |
521 | if (!rhiParams.inst) |
522 | qWarning(msg: "No QVulkanInstance set for QQuickWindow, this is wrong." ); |
523 | rhiParams.window = window; |
524 | rhi = QRhi::create(impl: backend, params: &rhiParams, flags); |
525 | } |
526 | #endif |
527 | #ifdef Q_OS_WIN |
528 | if (backend == QRhi::D3D11) { |
529 | QRhiD3D11InitParams rhiParams; |
530 | rhiParams.enableDebugLayer = isDebugLayerRequested(); |
531 | if (m_killDeviceFrameCount > 0) { |
532 | rhiParams.framesUntilKillingDeviceViaTdr = m_killDeviceFrameCount; |
533 | rhiParams.repeatDeviceKill = true; |
534 | } |
535 | rhi = QRhi::create(backend, &rhiParams, flags); |
536 | } |
537 | #endif |
538 | #if defined(Q_OS_MACOS) || defined(Q_OS_IOS) |
539 | if (backend == QRhi::Metal) { |
540 | QRhiMetalInitParams rhiParams; |
541 | rhi = QRhi::create(backend, &rhiParams, flags); |
542 | } |
543 | #endif |
544 | |
545 | if (!rhi) |
546 | qWarning(msg: "Failed to create RHI (backend %d)" , backend); |
547 | |
548 | return rhi; |
549 | } |
550 | |
551 | QImage QSGRhiSupport::grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain) |
552 | { |
553 | Q_ASSERT(rhi->isRecordingFrame()); |
554 | |
555 | QRhiReadbackResult result; |
556 | QRhiReadbackDescription readbackDesc; // read from swapchain backbuffer |
557 | QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch(); |
558 | resourceUpdates->readBackTexture(rb: readbackDesc, result: &result); |
559 | |
560 | swapchain->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates); |
561 | rhi->finish(); // make sure the readback has finished, stall the pipeline if needed |
562 | |
563 | // May be RGBA or BGRA. Plus premultiplied alpha. |
564 | QImage::Format imageFormat; |
565 | if (result.format == QRhiTexture::BGRA8) { |
566 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
567 | imageFormat = QImage::Format_ARGB32_Premultiplied; |
568 | #else |
569 | imageFormat = QImage::Format_RGBA8888_Premultiplied; |
570 | // ### and should swap too |
571 | #endif |
572 | } else { |
573 | imageFormat = QImage::Format_RGBA8888_Premultiplied; |
574 | } |
575 | |
576 | const uchar *p = reinterpret_cast<const uchar *>(result.data.constData()); |
577 | const QImage img(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat); |
578 | |
579 | if (rhi->isYUpInFramebuffer()) |
580 | return img.mirrored(); |
581 | |
582 | return img.copy(); |
583 | } |
584 | |
585 | QSGRhiProfileConnection *QSGRhiProfileConnection::instance() |
586 | { |
587 | static QSGRhiProfileConnection inst; |
588 | return &inst; |
589 | } |
590 | |
591 | void QSGRhiProfileConnection::initialize(QRhi *rhi) |
592 | { |
593 | #ifdef RHI_REMOTE_PROFILER |
594 | const QString profHost = qEnvironmentVariable(varName: "QSG_RHI_PROFILE_HOST" ); |
595 | if (!profHost.isEmpty()) { |
596 | int profPort = qEnvironmentVariableIntValue(varName: "QSG_RHI_PROFILE_PORT" ); |
597 | if (!profPort) |
598 | profPort = 30667; |
599 | qCDebug(QSG_LOG_INFO, "Sending RHI profiling output to %s:%d" , qPrintable(profHost), profPort); |
600 | m_profConn.reset(other: new QTcpSocket); |
601 | QObject::connect(sender: m_profConn.data(), signal: &QAbstractSocket::errorOccurred, context: m_profConn.data(), |
602 | slot: [this](QAbstractSocket::SocketError socketError) { qWarning(msg: " RHI profiler error: %d (%s)" , |
603 | socketError, qPrintable(m_profConn->errorString())); }); |
604 | m_profConn->connectToHost(hostName: profHost, port: profPort); |
605 | m_profConn->waitForConnected(); // blocking wait because we want to send stuff already from the init below |
606 | rhi->profiler()->setDevice(m_profConn.data()); |
607 | m_lastMemStatWrite.start(); |
608 | } |
609 | #else |
610 | Q_UNUSED(rhi); |
611 | #endif |
612 | } |
613 | |
614 | void QSGRhiProfileConnection::cleanup() |
615 | { |
616 | #ifdef RHI_REMOTE_PROFILER |
617 | m_profConn.reset(); |
618 | #endif |
619 | } |
620 | |
621 | void QSGRhiProfileConnection::send(QRhi *rhi) |
622 | { |
623 | #ifdef RHI_REMOTE_PROFILER |
624 | if (m_profConn) { |
625 | // do this every 5 sec at most |
626 | if (m_lastMemStatWrite.elapsed() >= 5000) { |
627 | rhi->profiler()->addVMemAllocatorStats(); |
628 | m_lastMemStatWrite.restart(); |
629 | } |
630 | } |
631 | #else |
632 | Q_UNUSED(rhi); |
633 | #endif |
634 | } |
635 | |
636 | QT_END_NAMESPACE |
637 | |