rhi: Enable specifying just an adapter or phys dev

Required by OpenXR. A VkPhysicalDevice or an adapter LUID + feature
level pair should be adoptable while leaving the rest (device, queue,
etc. setup) to QRhi as normal.

Change-Id: Iada0972671b037b4efb03e7831b7c9b8c5f2393d
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2020-06-24 15:59:56 +02:00
parent b60f516aca
commit db61e43c81
9 changed files with 284 additions and 71 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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];
} 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);
}
queryQueueFamilyProps();
gfxQueue = VK_NULL_HANDLE;
// 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,17 +582,20 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
}
if (gfxQueueFamilyIdx != -1) {
if (!gfxQueue)
df->vkGetDeviceQueue(dev, uint32_t(gfxQueueFamilyIdx), 0, &gfxQueue);
if (queueFamilyProps.isEmpty())
queryQueueFamilyProps();
hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
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;
}
df->vkGetDeviceQueue(dev, uint32_t(gfxQueueFamilyIdx), gfxQueueIdx, &gfxQueue);
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;
// helps little with an optimal offset of 1 (on some drivers) when the spec
@ -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;

View File

@ -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;
};

View File

@ -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;

View File

@ -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();