diff options
-rw-r--r-- | src/gui/rhi/qrhid3d11.cpp | 82 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11_p.h | 8 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11_p_p.h | 5 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal.mm | 12 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal_p.h | 13 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan.cpp | 97 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan_p.h | 4 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan_p_p.h | 3 | ||||
-rw-r--r-- | tests/auto/gui/rhi/qrhi/tst_qrhi.cpp | 131 |
9 files changed, 284 insertions, 71 deletions
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 38f2993049..6157381acb 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -84,10 +84,15 @@ QT_BEGIN_NAMESPACE When interoperating with another graphics engine, it may be necessary to get a QRhi instance that uses the same Direct3D device. This can be achieved by passing a pointer to a QRhiD3D11NativeHandles to - QRhi::create(). Both the device and the device context must be set to a - non-null value then. + QRhi::create(). When the device is set to a non-null value, the device + context must be specified as well. QRhi does not take ownership of any of + the external objects. - The QRhi does not take ownership of any of the external objects. + Sometimes, for example when using QRhi in combination with OpenXR, one will + want to specify which adapter to use, and optionally, which feature level + to request on the device, while leaving the device creation to QRhi. This + is achieved by leaving the device and context pointers set to null, while + specifying the adapter LUID and feature level. \note QRhi works with immediate contexts only. Deferred contexts are not used in any way. @@ -117,7 +122,7 @@ QT_BEGIN_NAMESPACE #define D3D11_1_UAV_SLOT_COUNT 64 #endif -QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice) +QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importParams) : ofr(this), deviceCurse(this) { @@ -126,22 +131,21 @@ QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *import deviceCurse.framesToActivate = params->framesUntilKillingDeviceViaTdr; deviceCurse.permanent = params->repeatDeviceKill; - importedDevice = importDevice != nullptr; - if (importedDevice) { - dev = reinterpret_cast<ID3D11Device *>(importDevice->dev); - if (dev) { - ID3D11DeviceContext *ctx = reinterpret_cast<ID3D11DeviceContext *>(importDevice->context); + if (importParams) { + if (importParams->dev && importParams->context) { + dev = reinterpret_cast<ID3D11Device *>(importParams->dev); + ID3D11DeviceContext *ctx = reinterpret_cast<ID3D11DeviceContext *>(importParams->context); if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast<void **>(&context)))) { // get rid of the ref added by QueryInterface ctx->Release(); + importedDeviceAndContext = true; } else { qWarning("ID3D11DeviceContext1 not supported by context, cannot import"); - importedDevice = false; } - } else { - qWarning("No ID3D11Device given, cannot import"); - importedDevice = false; } + featureLevel = D3D_FEATURE_LEVEL(importParams->featureLevel); + adapterLuid.LowPart = importParams->adapterLuidLow; + adapterLuid.HighPart = importParams->adapterLuidHigh; } } @@ -215,13 +219,28 @@ bool QRhiD3D11::create(QRhi::Flags flags) qCDebug(QRHI_LOG_INFO, "DXGI 1.2 = %s, FLIP_DISCARD swapchain supported = %s", hasDxgi2 ? "true" : "false", supportsFlipDiscardSwapchain ? "true" : "false"); - if (!importedDevice) { + if (!importedDeviceAndContext) { IDXGIAdapter1 *adapterToUse = nullptr; IDXGIAdapter1 *adapter; int requestedAdapterIndex = -1; if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX"); + // The importParams may specify an adapter by the luid, take that into account. + if (requestedAdapterIndex < 0 && (adapterLuid.LowPart || adapterLuid.HighPart)) { + for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { + DXGI_ADAPTER_DESC1 desc; + adapter->GetDesc1(&desc); + adapter->Release(); + if (desc.AdapterLuid.LowPart == adapterLuid.LowPart + && desc.AdapterLuid.HighPart == adapterLuid.HighPart) + { + requestedAdapterIndex = adapterIndex; + break; + } + } + } + if (requestedAdapterIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) { for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { DXGI_ADAPTER_DESC1 desc; @@ -246,6 +265,7 @@ bool QRhiD3D11::create(QRhi::Flags flags) desc.Flags); if (!adapterToUse && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) { adapterToUse = adapter; + adapterLuid = desc.AdapterLuid; qCDebug(QRHI_LOG_INFO, " using this adapter"); } else { adapter->Release(); @@ -256,9 +276,20 @@ bool QRhiD3D11::create(QRhi::Flags flags) return false; } + // Normally we won't specify a requested feature level list, + // except when a level was specified in importParams. + QVarLengthArray<D3D_FEATURE_LEVEL, 4> requestedFeatureLevels; + bool requestFeatureLevels = false; + if (featureLevel) { + requestFeatureLevels = true; + requestedFeatureLevels.append(featureLevel); + } + ID3D11DeviceContext *ctx = nullptr; HRESULT hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags, - nullptr, 0, D3D11_SDK_VERSION, + requestFeatureLevels ? requestedFeatureLevels.constData() : nullptr, + requestFeatureLevels ? requestedFeatureLevels.count() : 0, + D3D11_SDK_VERSION, &dev, &featureLevel, &ctx); // We cannot assume that D3D11_CREATE_DEVICE_DEBUG is always available. Retry without it, if needed. if (hr == DXGI_ERROR_SDK_COMPONENT_MISSING && debugLayer) { @@ -266,7 +297,9 @@ bool QRhiD3D11::create(QRhi::Flags flags) "Attempting to create D3D11 device without it."); devFlags &= ~D3D11_CREATE_DEVICE_DEBUG; hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags, - nullptr, 0, D3D11_SDK_VERSION, + requestFeatureLevels ? requestedFeatureLevels.constData() : nullptr, + requestFeatureLevels ? requestedFeatureLevels.count() : 0, + D3D11_SDK_VERSION, &dev, &featureLevel, &ctx); } adapterToUse->Release(); @@ -283,6 +316,18 @@ bool QRhiD3D11::create(QRhi::Flags flags) } else { Q_ASSERT(dev && context); featureLevel = dev->GetFeatureLevel(); + IDXGIDevice *dxgiDev = nullptr; + if (SUCCEEDED(dev->QueryInterface(IID_IDXGIDevice, reinterpret_cast<void **>(&dxgiDev)))) { + IDXGIAdapter *adapter = nullptr; + if (SUCCEEDED(dxgiDev->GetAdapter(&adapter))) { + DXGI_ADAPTER_DESC desc; + adapter->GetDesc(&desc); + adapterLuid = desc.AdapterLuid; + adapter->Release(); + } + dxgiDev->Release(); + } + qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); } if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast<void **>(&annotations)))) @@ -292,6 +337,9 @@ bool QRhiD3D11::create(QRhi::Flags flags) nativeHandlesStruct.dev = dev; nativeHandlesStruct.context = context; + nativeHandlesStruct.featureLevel = featureLevel; + nativeHandlesStruct.adapterLuidLow = adapterLuid.LowPart; + nativeHandlesStruct.adapterLuidHigh = adapterLuid.HighPart; if (deviceCurse.framesToActivate > 0) deviceCurse.initResources(); @@ -320,7 +368,7 @@ void QRhiD3D11::destroy() annotations = nullptr; } - if (!importedDevice) { + if (!importedDeviceAndContext) { if (context) { context->Release(); context = nullptr; diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h index aba0f37ee7..7823d811c3 100644 --- a/src/gui/rhi/qrhid3d11_p.h +++ b/src/gui/rhi/qrhid3d11_p.h @@ -50,8 +50,7 @@ #include <private/qrhi_p.h> -// no d3d includes here, to prevent precompiled header mess (due to this being -// a public header) +// no d3d includes here, to prevent precompiled header mess due to COM QT_BEGIN_NAMESPACE @@ -65,8 +64,13 @@ struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles { + // to import a device and a context void *dev = nullptr; void *context = nullptr; + // alternatively, to specify the device feature level and/or the adapter to use + int featureLevel = 0; + quint32 adapterLuidLow = 0; + qint32 adapterLuidHigh = 0; }; QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 5c0936c062..cf76195d84 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -670,10 +670,11 @@ public: void clearShaderCache(); bool debugLayer = false; - bool importedDevice = false; + bool importedDeviceAndContext = false; ID3D11Device *dev = nullptr; ID3D11DeviceContext1 *context = nullptr; - D3D_FEATURE_LEVEL featureLevel; + D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL(0); + LUID adapterLuid = {}; ID3DUserDefinedAnnotation *annotations = nullptr; IDXGIFactory1 *dxgiFactory = nullptr; bool hasDxgi2 = false; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 46a121c812..357e9e792d 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -107,10 +107,6 @@ QT_BEGIN_NAMESPACE \class QRhiMetalNativeHandles \inmodule QtRhi \brief Holds the Metal device used by the QRhi. - - \note The class uses \c{void *} as the type since including the Objective C - headers is not acceptable here. The actual types are \c{id<MTLDevice>} and - \c{id<MTLCommandQueue>}. */ /*! @@ -413,8 +409,8 @@ bool QRhiMetal::create(QRhi::Flags flags) } #endif - nativeHandlesStruct.dev = d->dev; - nativeHandlesStruct.cmdQueue = d->cmdQueue; + nativeHandlesStruct.dev = (MTLDevice *) d->dev; + nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue; return true; } @@ -3657,8 +3653,8 @@ void QMetalCommandBuffer::destroy() const QRhiNativeHandles *QMetalCommandBuffer::nativeHandles() { - nativeHandlesStruct.commandBuffer = d->cb; - nativeHandlesStruct.encoder = d->currentRenderPassEncoder; + nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb; + nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder; return &nativeHandlesStruct; } diff --git a/src/gui/rhi/qrhimetal_p.h b/src/gui/rhi/qrhimetal_p.h index 17e28b2c0f..c5c1eb08f7 100644 --- a/src/gui/rhi/qrhimetal_p.h +++ b/src/gui/rhi/qrhimetal_p.h @@ -50,7 +50,10 @@ #include <private/qrhi_p.h> -// no Metal includes here, the user code may be plain C++ +Q_FORWARD_DECLARE_OBJC_CLASS(MTLDevice); +Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandQueue); +Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandBuffer); +Q_FORWARD_DECLARE_OBJC_CLASS(MTLRenderCommandEncoder); QT_BEGIN_NAMESPACE @@ -60,14 +63,14 @@ struct Q_GUI_EXPORT QRhiMetalInitParams : public QRhiInitParams struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles { - void *dev = nullptr; // id<MTLDevice> - void *cmdQueue = nullptr; // id<MTLCommandQueue> + MTLDevice *dev = nullptr; + MTLCommandQueue *cmdQueue = nullptr; }; struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles { - void *commandBuffer = nullptr; // id<MTLCommandBuffer> - void *encoder = nullptr; // id<MTLRenderCommandEncoder> + MTLCommandBuffer *commandBuffer = nullptr; + MTLRenderCommandEncoder *encoder = nullptr; }; QT_END_NAMESPACE diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index ad6f5ebd8b..06fd6dc719 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -159,12 +159,19 @@ QT_BEGIN_NAMESPACE get a QRhi instance that uses the same Vulkan device. This can be achieved by passing a pointer to a QRhiVulkanNativeHandles to QRhi::create(). - The physical device and device object must then be set to a non-null value. - In addition, either the graphics queue family index or the graphics queue - object itself is required. Prefer the former, whenever possible since - deducing the index is not possible afterwards. Optionally, an existing - command pool object can be specified as well, and, also optionally, - vmemAllocator can be used to share the same + The physical device must always be set to a non-null value. If the + intention is to just specify a physical device, but leave the rest of the + VkDevice and queue creation to QRhi, then no other members need to be + filled out in the struct. For example, this is the case when working with + OpenXR. + + To adopt an existing \c VkDevice, the device field must be set to a + non-null value as well. In addition, the graphics queue family index is + required. The queue index is optional, as the default of 0 is often + suitable. + + Optionally, an existing command pool object can be specified as well. Also + optionally, vmemAllocator can be used to share the same \l{https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator}{Vulkan memory allocator} between two QRhi instances. @@ -298,31 +305,29 @@ static inline VmaAllocator toVmaAllocator(QVkAllocator a) return reinterpret_cast<VmaAllocator>(a); } -QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice) +QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importParams) : ofr(this) { inst = params->inst; maybeWindow = params->window; // may be null requestedDeviceExtensions = params->deviceExtensions; - importedDevice = importDevice != nullptr; - if (importedDevice) { - physDev = importDevice->physDev; - dev = importDevice->dev; - if (physDev && dev) { - gfxQueueFamilyIdx = importDevice->gfxQueueFamilyIdx; - gfxQueue = importDevice->gfxQueue; - if (importDevice->cmdPool) { + if (importParams) { + physDev = importParams->physDev; + dev = importParams->dev; + if (dev && physDev) { + importedDevice = true; + gfxQueueFamilyIdx = importParams->gfxQueueFamilyIdx; + gfxQueueIdx = importParams->gfxQueueIdx; + // gfxQueue is output only, no point in accepting it as input + if (importParams->cmdPool) { importedCmdPool = true; - cmdPool = importDevice->cmdPool; + cmdPool = importParams->cmdPool; } - if (importDevice->vmemAllocator) { + if (importParams->vmemAllocator) { importedAllocator = true; - allocator = importDevice->vmemAllocator; + allocator = importParams->vmemAllocator; } - } else { - qWarning("No (physical) Vulkan device is given, cannot import"); - importedDevice = false; } } } @@ -379,7 +384,8 @@ bool QRhiVulkan::create(QRhi::Flags flags) f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data()); }; - if (!importedDevice) { + // Choose a physical device, unless one was provided in importParams. + if (!physDev) { uint32_t physDevCount = 0; f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr); if (!physDevCount) { @@ -433,16 +439,29 @@ bool QRhiVulkan::create(QRhi::Flags flags) return false; } physDev = physDevs[physDevIndex]; - - queryQueueFamilyProps(); - - gfxQueue = VK_NULL_HANDLE; - + } else { + f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties); + qCDebug(QRHI_LOG_INFO, "Using imported physical device '%s' %d.%d.%d (api %d.%d.%d vendor 0x%X device 0x%X type %d)", + physDevProperties.deviceName, + VK_VERSION_MAJOR(physDevProperties.driverVersion), + VK_VERSION_MINOR(physDevProperties.driverVersion), + VK_VERSION_PATCH(physDevProperties.driverVersion), + VK_VERSION_MAJOR(physDevProperties.apiVersion), + VK_VERSION_MINOR(physDevProperties.apiVersion), + VK_VERSION_PATCH(physDevProperties.apiVersion), + physDevProperties.vendorID, + physDevProperties.deviceID, + physDevProperties.deviceType); + } + + // Choose queue and create device, unless the device was specified in importParams. + if (!importedDevice) { // We only support combined graphics+present queues. When it comes to // compute, only combined graphics+compute queue is used, compute gets // disabled otherwise. gfxQueueFamilyIdx = -1; int computelessGfxQueueCandidateIdx = -1; + queryQueueFamilyProps(); for (int i = 0; i < queueFamilyProps.count(); ++i) { qCDebug(QRHI_LOG_INFO, "queue family %d: flags=0x%x count=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount); @@ -540,11 +559,13 @@ bool QRhiVulkan::create(QRhi::Flags flags) devInfo.enabledExtensionCount = uint32_t(requestedDevExts.count()); devInfo.ppEnabledExtensionNames = requestedDevExts.constData(); - err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev); + VkResult err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev); if (err != VK_SUCCESS) { qWarning("Failed to create device: %d", err); return false; } + } else { + qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); } df = inst->deviceFunctions(dev); @@ -561,16 +582,19 @@ bool QRhiVulkan::create(QRhi::Flags flags) } } - if (gfxQueueFamilyIdx != -1) { - if (!gfxQueue) - df->vkGetDeviceQueue(dev, uint32_t(gfxQueueFamilyIdx), 0, &gfxQueue); + if (gfxQueueFamilyIdx < 0) { + // this is when importParams is faulty and did not specify the queue family index + qWarning("No queue family index provided"); + return false; + } - if (queueFamilyProps.isEmpty()) - queryQueueFamilyProps(); + df->vkGetDeviceQueue(dev, uint32_t(gfxQueueFamilyIdx), gfxQueueIdx, &gfxQueue); - hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0; - timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits; - } + if (queueFamilyProps.isEmpty()) + queryQueueFamilyProps(); + + hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0; + timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits; f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties); ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment; @@ -651,6 +675,7 @@ bool QRhiVulkan::create(QRhi::Flags flags) nativeHandlesStruct.physDev = physDev; nativeHandlesStruct.dev = dev; nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx; + nativeHandlesStruct.gfxQueueIdx = gfxQueueIdx; nativeHandlesStruct.gfxQueue = gfxQueue; nativeHandlesStruct.cmdPool = cmdPool; nativeHandlesStruct.vmemAllocator = allocator; diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h index f8870dcd77..b603b33e6d 100644 --- a/src/gui/rhi/qrhivulkan_p.h +++ b/src/gui/rhi/qrhivulkan_p.h @@ -62,10 +62,14 @@ struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles { + // to import a physical device (always required) VkPhysicalDevice physDev = VK_NULL_HANDLE; + // to import a device and queue VkDevice dev = VK_NULL_HANDLE; int gfxQueueFamilyIdx = -1; + int gfxQueueIdx = 0; VkQueue gfxQueue = VK_NULL_HANDLE; + // and optionally, command pool and/or mem allocator VkCommandPool cmdPool = VK_NULL_HANDLE; void *vmemAllocator = nullptr; }; diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index 84caa1a627..5e6bfa6258 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -645,7 +645,7 @@ struct QVkSwapChain : public QRhiSwapChain class QRhiVulkan : public QRhiImplementation { public: - QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice = nullptr); + QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importParams = nullptr); bool create(QRhi::Flags flags) override; void destroy() override; @@ -824,6 +824,7 @@ public: bool importedCmdPool = false; VkCommandPool cmdPool = VK_NULL_HANDLE; int gfxQueueFamilyIdx = -1; + int gfxQueueIdx = 0; VkQueue gfxQueue = VK_NULL_HANDLE; bool hasCompute = false; quint32 timestampValidBits = 0; diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index 553a6ae7a9..f559b40cf6 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -43,6 +43,7 @@ #if QT_CONFIG(vulkan) # include <QVulkanInstance> +# include <QVulkanFunctions> # include <QtGui/private/qrhivulkan_p.h> # define TST_VK #endif @@ -73,6 +74,9 @@ private slots: void create(); void nativeHandles_data(); void nativeHandles(); + void nativeHandlesImportVulkan(); + void nativeHandlesImportD3D11(); + void nativeHandlesImportOpenGL(); void nativeTexture_data(); void nativeTexture(); void nativeBuffer_data(); @@ -353,6 +357,7 @@ void tst_QRhi::nativeHandles() QVERIFY(vkHandles->physDev); QVERIFY(vkHandles->dev); QVERIFY(vkHandles->gfxQueueFamilyIdx >= 0); + QVERIFY(vkHandles->gfxQueueIdx >= 0); QVERIFY(vkHandles->gfxQueue); QVERIFY(vkHandles->cmdPool); QVERIFY(vkHandles->vmemAllocator); @@ -378,6 +383,8 @@ void tst_QRhi::nativeHandles() const QRhiD3D11NativeHandles *d3dHandles = static_cast<const QRhiD3D11NativeHandles *>(rhiHandles); QVERIFY(d3dHandles->dev); QVERIFY(d3dHandles->context); + QVERIFY(d3dHandles->featureLevel > 0); + QVERIFY(d3dHandles->adapterLuidLow || d3dHandles->adapterLuidHigh); } break; #endif @@ -494,6 +501,130 @@ void tst_QRhi::nativeHandles() } } +void tst_QRhi::nativeHandlesImportVulkan() +{ +#ifdef TST_VK + // VkDevice and everything else. For simplicity we'll get QRhi to create one, and then use that with another QRhi. + { + QScopedPointer<QRhi> rhi(QRhi::create(QRhi::Vulkan, &initParams.vk, QRhi::Flags(), nullptr)); + if (!rhi) + QSKIP("Skipping native Vulkan test"); + + const QRhiVulkanNativeHandles *nativeHandles = static_cast<const QRhiVulkanNativeHandles *>(rhi->nativeHandles()); + QRhiVulkanNativeHandles h = *nativeHandles; + // do not pass the rarely used fields, this is useful to test if it creates its own as expected + h.cmdPool = VK_NULL_HANDLE; + h.vmemAllocator = nullptr; + + QScopedPointer<QRhi> adoptingRhi(QRhi::create(QRhi::Vulkan, &initParams.vk, QRhi::Flags(), &h)); + QVERIFY(adoptingRhi); + + const QRhiVulkanNativeHandles *newNativeHandles = static_cast<const QRhiVulkanNativeHandles *>(adoptingRhi->nativeHandles()); + QCOMPARE(newNativeHandles->physDev, nativeHandles->physDev); + QCOMPARE(newNativeHandles->dev, nativeHandles->dev); + QCOMPARE(newNativeHandles->gfxQueueFamilyIdx, nativeHandles->gfxQueueFamilyIdx); + QCOMPARE(newNativeHandles->gfxQueueIdx, nativeHandles->gfxQueueIdx); + QVERIFY(newNativeHandles->cmdPool != nativeHandles->cmdPool); + QVERIFY(newNativeHandles->vmemAllocator != nativeHandles->vmemAllocator); + } + + // Physical device only + { + uint32_t physDevCount = 0; + QVulkanFunctions *f = vulkanInstance.functions(); + f->vkEnumeratePhysicalDevices(vulkanInstance.vkInstance(), &physDevCount, nullptr); + if (physDevCount < 1) + QSKIP("No Vulkan physical devices, skip"); + QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount); + f->vkEnumeratePhysicalDevices(vulkanInstance.vkInstance(), &physDevCount, physDevs.data()); + + for (uint32_t i = 0; i < physDevCount; ++i) { + QRhiVulkanNativeHandles h; + h.physDev = physDevs[i]; + QScopedPointer<QRhi> rhi(QRhi::create(QRhi::Vulkan, &initParams.vk, QRhi::Flags(), &h)); + // ok if fails, what we want to know is that if it succeeds, it must use that given phys.dev. + if (!rhi) { + qWarning("Skipping native Vulkan handle test for physical device %u", i); + continue; + } + const QRhiVulkanNativeHandles *actualNativeHandles = static_cast<const QRhiVulkanNativeHandles *>(rhi->nativeHandles()); + QCOMPARE(actualNativeHandles->physDev, physDevs[i]); + } + } + +#else + QSKIP("Skipping Vulkan-specific test"); +#endif +} + +void tst_QRhi::nativeHandlesImportD3D11() +{ +#ifdef TST_D3D11 + QScopedPointer<QRhi> rhi(QRhi::create(QRhi::D3D11, &initParams.d3d, QRhi::Flags(), nullptr)); + if (!rhi) + QSKIP("QRhi could not be created, skipping testing D3D11 native handle import"); + + const QRhiD3D11NativeHandles *nativeHandles = static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles()); + + // Case 1: device and context + { + QRhiD3D11NativeHandles h = *nativeHandles; + h.featureLevel = 0; // see if these are queried as expected, even when not provided + h.adapterLuidLow = 0; + h.adapterLuidHigh = 0; + QScopedPointer<QRhi> adoptingRhi(QRhi::create(QRhi::D3D11, &initParams.d3d, QRhi::Flags(), &h)); + QVERIFY(adoptingRhi); + const QRhiD3D11NativeHandles *newNativeHandles = static_cast<const QRhiD3D11NativeHandles *>(adoptingRhi->nativeHandles()); + QCOMPARE(newNativeHandles->dev, nativeHandles->dev); + QCOMPARE(newNativeHandles->context, nativeHandles->context); + QCOMPARE(newNativeHandles->featureLevel, nativeHandles->featureLevel); + QCOMPARE(newNativeHandles->adapterLuidLow, nativeHandles->adapterLuidLow); + QCOMPARE(newNativeHandles->adapterLuidHigh, nativeHandles->adapterLuidHigh); + } + + // Case 2: adapter and feature level only (hello OpenXR) + { + QRhiD3D11NativeHandles h = *nativeHandles; + h.dev = nullptr; + h.context = nullptr; + QScopedPointer<QRhi> adoptingRhi(QRhi::create(QRhi::D3D11, &initParams.d3d, QRhi::Flags(), &h)); + QVERIFY(adoptingRhi); + const QRhiD3D11NativeHandles *newNativeHandles = static_cast<const QRhiD3D11NativeHandles *>(adoptingRhi->nativeHandles()); + QVERIFY(newNativeHandles->dev != nativeHandles->dev); + QVERIFY(newNativeHandles->context != nativeHandles->context); + QCOMPARE(newNativeHandles->featureLevel, nativeHandles->featureLevel); + QCOMPARE(newNativeHandles->adapterLuidLow, nativeHandles->adapterLuidLow); + QCOMPARE(newNativeHandles->adapterLuidHigh, nativeHandles->adapterLuidHigh); + } + +#else + QSKIP("Skipping D3D11-specific test"); +#endif +} + +void tst_QRhi::nativeHandlesImportOpenGL() +{ +#ifdef TST_GL + QRhiGles2NativeHandles h; + QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext); + ctx->setFormat(QRhiGles2InitParams::adjustedFormat()); + if (!ctx->create()) + QSKIP("No OpenGL context, skipping OpenGL-specific test"); + h.context = ctx.data(); + QScopedPointer<QRhi> rhi(QRhi::create(QRhi::OpenGLES2, &initParams.gl, QRhi::Flags(), &h)); + if (!rhi) + QSKIP("QRhi could not be created, skipping testing OpenGL native handle import"); + + const QRhiGles2NativeHandles *actualNativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); + QCOMPARE(actualNativeHandles->context, ctx.data()); + + rhi->makeThreadLocalNativeContextCurrent(); + QCOMPARE(QOpenGLContext::currentContext(), ctx.data()); +#else + QSKIP("Skipping OpenGL-specific test"); +#endif +} + void tst_QRhi::nativeTexture_data() { rhiTestData(); |