rhi: Introduce a way to enumerate adapters/physical devices
Requesting a given "adapter" (or whatever the native equivalent of that is, e.g. a VkPhysicalDevice with Vulkan) has already been possible either via the index-based environment-variables (QT_D3D_ADAPTER_INDEX and QT_VK_PHYSICAL_DEVICE_INDEX), or by passing in a VkPhysicalDevice in QRhiVulkanNativeHandles or an adapter LUID in QD3D1xNativeHandles to create(). (the latter is what QQuickGraphicsDevice's fromAdapter() or fromPhysicalDevice() builds on, and is one of the enablers for the OpenXR integration in Qt Quick 3D for instance) There was however no way to enumerate the adapters via QRhi APIs, in order to implement something such as "iterate through the available adapters and choose one from the list", without resorting directly to DXGI or Vulkan. This is what enumerateAdapters() enables now. Implemented for D3D11, D3D12, and Vulkan. There are no plans to implement it for others for now. (meaning the resulting list is always empty then) For symmetry, it is also possible to get a single QRhiAdapter based on a provided native adapter identity (i.e, LUID or VkPhysicalDevice), although in practice that is just a heavier way to achieve the same that was already possible by passing those native handles directly to create() instead of going through a QRhiAdapter. [ChangeLog][RHI] Introduced enumerateAdapters() in QRhi to provide a an abstraction for enumerating adapters (physical devices) with Direct 3D and Vulkan. Fixes: QTBUG-129932 Change-Id: I072553afe594cbad6ebfa1ffe849a782c4c181db Reviewed-by: Andy Nichols <andy.nichols@qt.io> Reviewed-by: Kristoffer Skau <kristoffer.skau@qt.io>
This commit is contained in:
parent
d8f75f34c9
commit
2b451c81a3
@ -8767,9 +8767,76 @@ QRhi::~QRhi()
|
||||
delete d;
|
||||
}
|
||||
|
||||
QRhiImplementation *QRhiImplementation::newInstance(QRhi::Implementation impl, QRhiInitParams *params, QRhiNativeHandles *importDevice)
|
||||
{
|
||||
QRhiImplementation *d = nullptr;
|
||||
|
||||
switch (impl) {
|
||||
case QRhi::Null:
|
||||
d = new QRhiNull(static_cast<QRhiNullInitParams *>(params));
|
||||
break;
|
||||
case QRhi::Vulkan:
|
||||
#if QT_CONFIG(vulkan)
|
||||
d = new QRhiVulkan(static_cast<QRhiVulkanInitParams *>(params),
|
||||
static_cast<QRhiVulkanNativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
Q_UNUSED(importDevice);
|
||||
qWarning("This build of Qt has no Vulkan support");
|
||||
break;
|
||||
#endif
|
||||
case QRhi::OpenGLES2:
|
||||
#ifndef QT_NO_OPENGL
|
||||
d = new QRhiGles2(static_cast<QRhiGles2InitParams *>(params),
|
||||
static_cast<QRhiGles2NativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("This build of Qt has no OpenGL support");
|
||||
break;
|
||||
#endif
|
||||
case QRhi::D3D11:
|
||||
#ifdef Q_OS_WIN
|
||||
d = new QRhiD3D11(static_cast<QRhiD3D11InitParams *>(params),
|
||||
static_cast<QRhiD3D11NativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("This platform has no Direct3D 11 support");
|
||||
break;
|
||||
#endif
|
||||
case QRhi::Metal:
|
||||
#if QT_CONFIG(metal)
|
||||
d = new QRhiMetal(static_cast<QRhiMetalInitParams *>(params),
|
||||
static_cast<QRhiMetalNativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("This platform has no Metal support");
|
||||
break;
|
||||
#endif
|
||||
case QRhi::D3D12:
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef QRHI_D3D12_AVAILABLE
|
||||
d = new QRhiD3D12(static_cast<QRhiD3D12InitParams *>(params),
|
||||
static_cast<QRhiD3D12NativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("Qt was built without Direct3D 12 support. "
|
||||
"This is likely due to having ancient SDK headers (such as d3d12.h) in the Qt build environment. "
|
||||
"Rebuild Qt with an SDK supporting D3D12 features introduced in Windows 10 version 1703, "
|
||||
"or use an MSVC build as those typically are built with more up-to-date SDKs.");
|
||||
break;
|
||||
#endif
|
||||
#else
|
||||
qWarning("This platform has no Direct3D 12 support");
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
bool QRhiImplementation::rubLogEnabled = false;
|
||||
|
||||
void QRhiImplementation::prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags)
|
||||
void QRhiImplementation::prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags, QRhiAdapter *adapter)
|
||||
{
|
||||
q = rhi;
|
||||
|
||||
@ -8785,6 +8852,13 @@ void QRhiImplementation::prepareForCreate(QRhi *rhi, QRhi::Implementation impl,
|
||||
|
||||
implType = impl;
|
||||
implThread = QThread::currentThread();
|
||||
|
||||
requestedRhiAdapter = adapter;
|
||||
}
|
||||
|
||||
QRhi::AdapterList QRhiImplementation::enumerateAdaptersBeforeCreate(QRhiNativeHandles *) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -8816,79 +8890,31 @@ void QRhiImplementation::prepareForCreate(QRhi *rhi, QRhi::Implementation impl,
|
||||
QRhiMetalNativeHandles, QRhiGles2NativeHandles. The exact details and
|
||||
semantics depend on the backand and the underlying graphics API.
|
||||
|
||||
Specifying a QRhiAdapter in \a adapter offers a transparent, cross-API
|
||||
alternative to passing in a \c VkPhysicalDevice via QRhiVulkanNativeHandles,
|
||||
or an adapter LUID via QRhiD3D12NativeHandles. The ownership of \a adapter
|
||||
is not taken. See enumerateAdapters() for more information on this approach.
|
||||
|
||||
\note \a importDevice and \a adapter cannot be both specified.
|
||||
|
||||
\sa probe()
|
||||
*/
|
||||
QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRhiNativeHandles *importDevice)
|
||||
QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRhiNativeHandles *importDevice, QRhiAdapter *adapter)
|
||||
{
|
||||
std::unique_ptr<QRhi> r(new QRhi);
|
||||
|
||||
switch (impl) {
|
||||
case Null:
|
||||
r->d = new QRhiNull(static_cast<QRhiNullInitParams *>(params));
|
||||
break;
|
||||
case Vulkan:
|
||||
#if QT_CONFIG(vulkan)
|
||||
r->d = new QRhiVulkan(static_cast<QRhiVulkanInitParams *>(params),
|
||||
static_cast<QRhiVulkanNativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
Q_UNUSED(importDevice);
|
||||
qWarning("This build of Qt has no Vulkan support");
|
||||
break;
|
||||
#endif
|
||||
case OpenGLES2:
|
||||
#ifndef QT_NO_OPENGL
|
||||
r->d = new QRhiGles2(static_cast<QRhiGles2InitParams *>(params),
|
||||
static_cast<QRhiGles2NativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("This build of Qt has no OpenGL support");
|
||||
break;
|
||||
#endif
|
||||
case D3D11:
|
||||
#ifdef Q_OS_WIN
|
||||
r->d = new QRhiD3D11(static_cast<QRhiD3D11InitParams *>(params),
|
||||
static_cast<QRhiD3D11NativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("This platform has no Direct3D 11 support");
|
||||
break;
|
||||
#endif
|
||||
case Metal:
|
||||
#if QT_CONFIG(metal)
|
||||
r->d = new QRhiMetal(static_cast<QRhiMetalInitParams *>(params),
|
||||
static_cast<QRhiMetalNativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("This platform has no Metal support");
|
||||
break;
|
||||
#endif
|
||||
case D3D12:
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef QRHI_D3D12_AVAILABLE
|
||||
r->d = new QRhiD3D12(static_cast<QRhiD3D12InitParams *>(params),
|
||||
static_cast<QRhiD3D12NativeHandles *>(importDevice));
|
||||
break;
|
||||
#else
|
||||
qWarning("Qt was built without Direct3D 12 support. "
|
||||
"This is likely due to having ancient SDK headers (such as d3d12.h) in the Qt build environment. "
|
||||
"Rebuild Qt with an SDK supporting D3D12 features introduced in Windows 10 version 1703, "
|
||||
"or use an MSVC build as those typically are built with more up-to-date SDKs.");
|
||||
break;
|
||||
#endif
|
||||
#else
|
||||
qWarning("This platform has no Direct3D 12 support");
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (r->d) {
|
||||
r->d->prepareForCreate(r.get(), impl, flags);
|
||||
if (r->d->create(flags))
|
||||
return r.release();
|
||||
}
|
||||
if (adapter && importDevice)
|
||||
qWarning("adapter and importDevice should not both be non-null in QRhi::create()");
|
||||
|
||||
std::unique_ptr<QRhiImplementation> rd(QRhiImplementation::newInstance(impl, params, importDevice));
|
||||
if (!rd)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<QRhi> r(new QRhi);
|
||||
r->d = rd.release();
|
||||
r->d->prepareForCreate(r.get(), impl, flags, adapter);
|
||||
if (!r->d->create(flags))
|
||||
return nullptr;
|
||||
|
||||
return r.release();
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -8925,6 +8951,111 @@ bool QRhi::probe(QRhi::Implementation impl, QRhiInitParams *params)
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*!
|
||||
\typedef QRhi::AdapterList
|
||||
\relates QRhi
|
||||
\since 6.10
|
||||
|
||||
Synonym for QVector<QRhiAdapter *>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\return the list of adapters (physical devices) present, or an empty list
|
||||
when such control is not available with a given graphics API.
|
||||
|
||||
Backends where such level of control is not available, the returned list is
|
||||
always empty. Thus an empty list does not indicate there are no graphics
|
||||
devices in the system, but that fine-grained control over selecting which
|
||||
one to use is not available.
|
||||
|
||||
Backends for Direct 3D 11, Direct 3D 12, and Vulkan can be expected to fully
|
||||
support enumerating adapters. Others may not. The backend is specified by \a
|
||||
impl. A QRhiAdapter returned from this function must only be used in a
|
||||
create() call with the same \a impl. Some underlying APIs may present
|
||||
further limitations, with Vulkan in particular the QRhiAdapter is specified
|
||||
to the QVulkanInstance (\c VkInstance).
|
||||
|
||||
The caller is expected to destroy the QRhiAdapter objects in the list. Apart
|
||||
from querying \l{QRhiAdapter::}info(), the only purpose of these objects is
|
||||
to be passed on to create(), or the corresponding functions in higher layers
|
||||
such as Qt Quick.
|
||||
|
||||
The following snippet, written specifically for Vulkan, shows how to
|
||||
enumerate the available physical devices and request to create a QRhi for
|
||||
the chosen one. This in practice is equivalent to passing in a \c
|
||||
VkPhysicalDevice via a QRhiVulkanNativeHandles to create(), but it involves
|
||||
less API-specific code on the application side:
|
||||
|
||||
\code
|
||||
QRhiVulkanInitParams initParams;
|
||||
initParams.inst = &vulkanInstance;
|
||||
QRhi::AdapterList adapters = QRhi::enumerateAdapters(QRhi::Vulkan, &initParams);
|
||||
QRhiAdapter *chosenAdapter = nullptr;
|
||||
for (QRhiAdapter *adapter : adapters) {
|
||||
if (looksGood(adapter->info())) {
|
||||
chosenAdapter = adapter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QRhi *rhi = QRhi::create(QRhi::Vulkan, &initParams, {}, nullptr, chosenAdapter);
|
||||
qDeleteAll(adapters);
|
||||
\endcode
|
||||
|
||||
Passing in \a params is required due to some of the underlying graphics
|
||||
APIs' design. With Vulkan in particular, the QVulkanInstance must be
|
||||
provided, since enumerating is not possible without it. Other fields in the
|
||||
backend-specific \a params will not actually be used by this function.
|
||||
|
||||
\a nativeHandles is optional. When specified, it must be a valid
|
||||
QRhiD3D11NativeHandles, QRhiD3D12NativeHandles, or QRhiVulkanNativeHandles,
|
||||
similarly to create(). However, unlike create(), only the physical device
|
||||
(in case of Vulkan) or the adapter LUID (in case of D3D) fields are used,
|
||||
all other fields are ignored. This can be used the restrict the results to a
|
||||
given adapter. The returned list will contain 1 or 0 elements in this case.
|
||||
|
||||
Note how in the previous code snippet the looksGood() function
|
||||
implementation cannot perform any platform-specific filtering based on the
|
||||
true adapter / physical device identity, such as the adapter LUID on Windows
|
||||
or the VkPhysicalDevice with Vulkan. This is because QRhiDriverInfo does not
|
||||
contain platform-specific data. Instead, use \a nativeHandles to get the
|
||||
results filtered already inside enumerateAdapters().
|
||||
|
||||
The following two snippets, using Direct 3D 12 as an example, are equivalent
|
||||
in practice:
|
||||
|
||||
\code
|
||||
// enumerateAdapters-based approach from Qt 6.10 on
|
||||
QRhiD3D12InitParams initParams;
|
||||
QRhiD3D12NativeHandles nativeHandles;
|
||||
nativeHandles.adapterLuidLow = luid.LowPart; // retrieved a LUID from somewhere, now pass it on to Qt
|
||||
nativeHandles.adapterLuidHigh = luid.HighPart;
|
||||
QRhi::AdapterList adapters = QRhi::enumerateAdapters(QRhi::D3D12, &initParams, &nativeHandles);
|
||||
if (adapters.isEmpty()) { qWarning("Requested adapter was not found"); }
|
||||
QRhi *rhi = QRhi::create(QRhi::D3D12, &initParams, {}, nullptr, adapters[0]);
|
||||
qDeleteAll(adapters);
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// traditional approach, more lightweight
|
||||
QRhiD3D12InitParams initParams;
|
||||
QRhiD3D12NativeHandles nativeHandles;
|
||||
nativeHandles.adapterLuidLow = luid.LowPart; // retrieved a LUID from somewhere, now pass it on to Qt
|
||||
nativeHandles.adapterLuidHigh = luid.HighPart;
|
||||
QRhi *rhi = QRhi::create(QRhi::D3D12, &initParams, {}, &nativeHandles, nullptr);
|
||||
\endcode
|
||||
|
||||
\since 6.10
|
||||
\sa create()
|
||||
*/
|
||||
QRhi::AdapterList QRhi::enumerateAdapters(Implementation impl, QRhiInitParams *params, QRhiNativeHandles *nativeHandles)
|
||||
{
|
||||
std::unique_ptr<QRhiImplementation> rd(QRhiImplementation::newInstance(impl, params, nullptr));
|
||||
if (!rd)
|
||||
return {};
|
||||
|
||||
return rd->enumerateAdaptersBeforeCreate(nativeHandles);
|
||||
}
|
||||
|
||||
/*!
|
||||
\struct QRhiSwapChainProxyData
|
||||
\inmodule QtGuiPrivate
|
||||
@ -9118,6 +9249,45 @@ QRhiDriverInfo QRhi::driverInfo() const
|
||||
return d->driverInfo();
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QRhiAdapter
|
||||
\inmodule QtGuiPrivate
|
||||
\inheaderfile rhi/qrhi.h
|
||||
\since 6.10
|
||||
|
||||
\brief Represents a physical graphics device.
|
||||
|
||||
Some QRhi backends target graphics APIs that expose the concept of \c
|
||||
adapters or \c{physical devices}. Call the static \l
|
||||
{QRhi::}enumerateAdapters() function to retrieve a list of the adapters
|
||||
present in the system. Pass one of the returned QRhiAdapter objects to \l
|
||||
{QRhi::}create() in order to request using the adapter or physical device
|
||||
the QRhiAdapter corresponds to. Other than exposing the QRhiDriverInfo,
|
||||
QRhiAdapter is to be treated as an opaque handle.
|
||||
|
||||
\note With Vulkan, the QRhiAdapter is valid only as long as the
|
||||
QVulkanInstance that was used for \l{QRhi::}enumerateAdapters() is valid.
|
||||
This also means that a QRhiAdapter is tied to the Vulkan instance
|
||||
(QVulkanInstance, \c VkInstance) and cannot be used in the context of
|
||||
another Vulkan instance.
|
||||
|
||||
\note This is a RHI API with limited compatibility guarantees, see \l QRhi
|
||||
for details.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QRhiDriverInfo QRhiAdapter::info() const = 0
|
||||
|
||||
\return the corresponding QRhiDriverInfo.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QRhiAdapter::~QRhiAdapter()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\return the thread on which the QRhi was \l{QRhi::create()}{initialized}.
|
||||
*/
|
||||
|
@ -1832,6 +1832,13 @@ Q_DECLARE_TYPEINFO(QRhiStats, Q_RELOCATABLE_TYPE);
|
||||
Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiStats &);
|
||||
#endif
|
||||
|
||||
class Q_GUI_EXPORT QRhiAdapter
|
||||
{
|
||||
public:
|
||||
virtual ~QRhiAdapter();
|
||||
virtual QRhiDriverInfo info() const = 0;
|
||||
};
|
||||
|
||||
struct Q_GUI_EXPORT QRhiInitParams
|
||||
{
|
||||
};
|
||||
@ -1946,8 +1953,13 @@ public:
|
||||
static QRhi *create(Implementation impl,
|
||||
QRhiInitParams *params,
|
||||
Flags flags = {},
|
||||
QRhiNativeHandles *importDevice = nullptr);
|
||||
QRhiNativeHandles *importDevice = nullptr,
|
||||
QRhiAdapter *adapter = nullptr);
|
||||
static bool probe(Implementation impl, QRhiInitParams *params);
|
||||
using AdapterList = QVector<QRhiAdapter *>;
|
||||
static AdapterList enumerateAdapters(Implementation impl,
|
||||
QRhiInitParams *params,
|
||||
QRhiNativeHandles *nativeHandles = nullptr);
|
||||
|
||||
Implementation backend() const;
|
||||
const char *backendName() const;
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
|
||||
virtual bool create(QRhi::Flags flags) = 0;
|
||||
virtual void destroy() = 0;
|
||||
virtual QRhi::AdapterList enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const;
|
||||
|
||||
virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
|
||||
virtual QRhiComputePipeline *createComputePipeline() = 0;
|
||||
@ -146,7 +147,8 @@ public:
|
||||
virtual QByteArray pipelineCacheData() = 0;
|
||||
virtual void setPipelineCacheData(const QByteArray &data) = 0;
|
||||
|
||||
void prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags);
|
||||
static QRhiImplementation *newInstance(QRhi::Implementation impl, QRhiInitParams *params, QRhiNativeHandles *importDevice);
|
||||
void prepareForCreate(QRhi *rhi, QRhi::Implementation impl, QRhi::Flags flags, QRhiAdapter *adapter);
|
||||
|
||||
bool isCompressedFormat(QRhiTexture::Format format) const;
|
||||
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
|
||||
@ -247,6 +249,8 @@ public:
|
||||
int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
|
||||
bool inFrame = false;
|
||||
|
||||
QRhiAdapter *requestedRhiAdapter = nullptr;
|
||||
|
||||
private:
|
||||
QRhi::Implementation implType;
|
||||
QThread *implThread;
|
||||
|
@ -246,7 +246,10 @@ bool QRhiD3D11::create(QRhi::Flags flags)
|
||||
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 (requestedRhiAdapter)
|
||||
adapterLuid = static_cast<QD3D11Adapter *>(requestedRhiAdapter)->luid;
|
||||
|
||||
// importParams or requestedRhiAdapter may specify an adapter by the luid, use that in the absence of an env.var. override.
|
||||
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;
|
||||
@ -466,6 +469,9 @@ void QRhiD3D11::destroy()
|
||||
dxgiFactory = nullptr;
|
||||
}
|
||||
|
||||
importedDeviceAndContext = false;
|
||||
adapterLuid = {};
|
||||
|
||||
QDxgiVSyncService::instance()->derefAdapter(adapterLuid);
|
||||
}
|
||||
|
||||
@ -479,6 +485,48 @@ void QRhiD3D11::reportLiveObjects(ID3D11Device *device)
|
||||
}
|
||||
}
|
||||
|
||||
QRhi::AdapterList QRhiD3D11::enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const
|
||||
{
|
||||
LUID requestedLuid = {};
|
||||
if (nativeHandles) {
|
||||
QRhiD3D11NativeHandles *h = static_cast<QRhiD3D11NativeHandles *>(nativeHandles);
|
||||
const LUID adapterLuid = { h->adapterLuidLow, h->adapterLuidHigh };
|
||||
if (adapterLuid.LowPart || adapterLuid.HighPart)
|
||||
requestedLuid = adapterLuid;
|
||||
}
|
||||
|
||||
IDXGIFactory1 *dxgi = createDXGIFactory2();
|
||||
if (!dxgi)
|
||||
return {};
|
||||
|
||||
QRhi::AdapterList list;
|
||||
IDXGIAdapter1 *adapter;
|
||||
for (int adapterIndex = 0; dxgi->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
adapter->Release();
|
||||
if (requestedLuid.LowPart || requestedLuid.HighPart) {
|
||||
if (desc.AdapterLuid.LowPart != requestedLuid.LowPart
|
||||
|| desc.AdapterLuid.HighPart != requestedLuid.HighPart)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
QD3D11Adapter *a = new QD3D11Adapter;
|
||||
a->luid = desc.AdapterLuid;
|
||||
QRhiD3D::fillDriverInfo(&a->adapterInfo, desc);
|
||||
list.append(a);
|
||||
}
|
||||
|
||||
dxgi->Release();
|
||||
return list;
|
||||
}
|
||||
|
||||
QRhiDriverInfo QD3D11Adapter::info() const
|
||||
{
|
||||
return adapterInfo;
|
||||
}
|
||||
|
||||
QList<int> QRhiD3D11::supportedSampleCounts() const
|
||||
{
|
||||
return { 1, 2, 4, 8 };
|
||||
|
@ -628,6 +628,15 @@ struct QD3D11SwapChain : public QRhiSwapChain
|
||||
HANDLE frameLatencyWaitableObject = nullptr;
|
||||
};
|
||||
|
||||
class QD3D11Adapter : public QRhiAdapter
|
||||
{
|
||||
public:
|
||||
QRhiDriverInfo info() const override;
|
||||
|
||||
LUID luid;
|
||||
QRhiDriverInfo adapterInfo;
|
||||
};
|
||||
|
||||
class QRhiD3D11 : public QRhiImplementation
|
||||
{
|
||||
public:
|
||||
@ -635,6 +644,7 @@ public:
|
||||
|
||||
bool create(QRhi::Flags flags) override;
|
||||
void destroy() override;
|
||||
QRhi::AdapterList enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const override;
|
||||
|
||||
QRhiGraphicsPipeline *createGraphicsPipeline() override;
|
||||
QRhiComputePipeline *createComputePipeline() override;
|
||||
|
@ -255,7 +255,10 @@ bool QRhiD3D12::create(QRhi::Flags flags)
|
||||
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 (requestedRhiAdapter)
|
||||
adapterLuid = static_cast<QD3D12Adapter *>(requestedRhiAdapter)->luid;
|
||||
|
||||
// importParams or requestedRhiAdapter may specify an adapter by the luid, use that in the absence of an env.var. override.
|
||||
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;
|
||||
@ -610,9 +613,55 @@ void QRhiD3D12::destroy()
|
||||
dxgiFactory = nullptr;
|
||||
}
|
||||
|
||||
adapterLuid = {};
|
||||
importedDevice = false;
|
||||
importedCommandQueue = false;
|
||||
|
||||
QDxgiVSyncService::instance()->derefAdapter(adapterLuid);
|
||||
}
|
||||
|
||||
QRhi::AdapterList QRhiD3D12::enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const
|
||||
{
|
||||
LUID requestedLuid = {};
|
||||
if (nativeHandles) {
|
||||
QRhiD3D12NativeHandles *h = static_cast<QRhiD3D12NativeHandles *>(nativeHandles);
|
||||
const LUID adapterLuid = { h->adapterLuidLow, h->adapterLuidHigh };
|
||||
if (adapterLuid.LowPart || adapterLuid.HighPart)
|
||||
requestedLuid = adapterLuid;
|
||||
}
|
||||
|
||||
IDXGIFactory2 *dxgi = nullptr;
|
||||
if (FAILED(CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgi))))
|
||||
return {};
|
||||
|
||||
QRhi::AdapterList list;
|
||||
IDXGIAdapter1 *adapter;
|
||||
for (int adapterIndex = 0; dxgi->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
adapter->Release();
|
||||
if (requestedLuid.LowPart || requestedLuid.HighPart) {
|
||||
if (desc.AdapterLuid.LowPart != requestedLuid.LowPart
|
||||
|| desc.AdapterLuid.HighPart != requestedLuid.HighPart)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
QD3D12Adapter *a = new QD3D12Adapter;
|
||||
a->luid = desc.AdapterLuid;
|
||||
QRhiD3D::fillDriverInfo(&a->adapterInfo, desc);
|
||||
list.append(a);
|
||||
}
|
||||
|
||||
dxgi->Release();
|
||||
return list;
|
||||
}
|
||||
|
||||
QRhiDriverInfo QD3D12Adapter::info() const
|
||||
{
|
||||
return adapterInfo;
|
||||
}
|
||||
|
||||
QList<int> QRhiD3D12::supportedSampleCounts() const
|
||||
{
|
||||
return { 1, 2, 4, 8 };
|
||||
|
@ -1088,6 +1088,15 @@ struct alignas(void*) QD3D12PipelineStateSubObject
|
||||
T object = {};
|
||||
};
|
||||
|
||||
class QD3D12Adapter : public QRhiAdapter
|
||||
{
|
||||
public:
|
||||
QRhiDriverInfo info() const override;
|
||||
|
||||
LUID luid;
|
||||
QRhiDriverInfo adapterInfo;
|
||||
};
|
||||
|
||||
class QRhiD3D12 : public QRhiImplementation
|
||||
{
|
||||
public:
|
||||
@ -1101,6 +1110,7 @@ public:
|
||||
|
||||
bool create(QRhi::Flags flags) override;
|
||||
void destroy() override;
|
||||
QRhi::AdapterList enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const override;
|
||||
|
||||
QRhiGraphicsPipeline *createGraphicsPipeline() override;
|
||||
QRhiComputePipeline *createComputePipeline() override;
|
||||
|
@ -475,6 +475,14 @@ static inline QRhiDriverInfo::DeviceType toRhiDeviceType(VkPhysicalDeviceType ty
|
||||
}
|
||||
}
|
||||
|
||||
static inline void fillDriverInfo(QRhiDriverInfo *info, const VkPhysicalDeviceProperties &physDevProperties)
|
||||
{
|
||||
info->deviceName = QByteArray(physDevProperties.deviceName);
|
||||
info->deviceId = physDevProperties.deviceID;
|
||||
info->vendorId = physDevProperties.vendorID;
|
||||
info->deviceType = toRhiDeviceType(physDevProperties.deviceType);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline void addToChain(T *head, void *entry)
|
||||
{
|
||||
@ -539,6 +547,16 @@ bool QRhiVulkan::create(QRhi::Flags flags)
|
||||
if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX"))
|
||||
requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
|
||||
|
||||
if (requestedPhysDevIndex < 0 && requestedRhiAdapter) {
|
||||
VkPhysicalDevice requestedPhysDev = static_cast<QVulkanAdapter *>(requestedRhiAdapter)->physDev;
|
||||
for (int i = 0; i < int(physDevCount); ++i) {
|
||||
if (physDevs[i] == requestedPhysDev) {
|
||||
requestedPhysDevIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requestedPhysDevIndex < 0 && flags.testFlag(QRhi::PreferSoftwareRenderer)) {
|
||||
for (int i = 0; i < int(physDevCount); ++i) {
|
||||
f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
|
||||
@ -605,10 +623,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
|
||||
caps.apiVersion = physDevApiVersion;
|
||||
}
|
||||
|
||||
driverInfoStruct.deviceName = QByteArray(physDevProperties.deviceName);
|
||||
driverInfoStruct.deviceId = physDevProperties.deviceID;
|
||||
driverInfoStruct.vendorId = physDevProperties.vendorID;
|
||||
driverInfoStruct.deviceType = toRhiDeviceType(physDevProperties.deviceType);
|
||||
fillDriverInfo(&driverInfoStruct, physDevProperties);
|
||||
|
||||
QVulkanInfoVector<QVulkanExtension> devExts;
|
||||
uint32_t devExtCount = 0;
|
||||
@ -1118,6 +1133,49 @@ void QRhiVulkan::destroy()
|
||||
|
||||
f = nullptr;
|
||||
df = nullptr;
|
||||
|
||||
importedDevice = false;
|
||||
importedAllocator = false;
|
||||
}
|
||||
|
||||
QRhi::AdapterList QRhiVulkan::enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const
|
||||
{
|
||||
VkPhysicalDevice requestedPhysDev = VK_NULL_HANDLE;
|
||||
if (nativeHandles) {
|
||||
QRhiVulkanNativeHandles *h = static_cast<QRhiVulkanNativeHandles *>(nativeHandles);
|
||||
requestedPhysDev = h->physDev;
|
||||
}
|
||||
|
||||
QRhi::AdapterList list;
|
||||
QVulkanFunctions *f = inst->functions();
|
||||
uint32_t physDevCount = 0;
|
||||
f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
|
||||
if (!physDevCount)
|
||||
return {};
|
||||
|
||||
QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
|
||||
VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
|
||||
if (err != VK_SUCCESS || !physDevCount)
|
||||
return {};
|
||||
|
||||
VkPhysicalDeviceProperties physDevProperties = {};
|
||||
for (uint32_t i = 0; i < physDevCount; ++i) {
|
||||
if (requestedPhysDev && physDevs[i] != requestedPhysDev)
|
||||
continue;
|
||||
|
||||
f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
|
||||
QVulkanAdapter *a = new QVulkanAdapter;
|
||||
a->physDev = physDevs[i];
|
||||
fillDriverInfo(&a->adapterInfo, physDevProperties);
|
||||
list.append(a);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QRhiDriverInfo QVulkanAdapter::info() const
|
||||
{
|
||||
return adapterInfo;
|
||||
}
|
||||
|
||||
VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool)
|
||||
|
@ -672,6 +672,15 @@ struct QVkSwapChain : public QRhiSwapChain
|
||||
friend class QRhiVulkan;
|
||||
};
|
||||
|
||||
class QVulkanAdapter : public QRhiAdapter
|
||||
{
|
||||
public:
|
||||
QRhiDriverInfo info() const override;
|
||||
|
||||
VkPhysicalDevice physDev;
|
||||
QRhiDriverInfo adapterInfo;
|
||||
};
|
||||
|
||||
class QRhiVulkan : public QRhiImplementation
|
||||
{
|
||||
public:
|
||||
@ -679,6 +688,7 @@ public:
|
||||
|
||||
bool create(QRhi::Flags flags) override;
|
||||
void destroy() override;
|
||||
QRhi::AdapterList enumerateAdaptersBeforeCreate(QRhiNativeHandles *nativeHandles) const override;
|
||||
|
||||
QRhiGraphicsPipeline *createGraphicsPipeline() override;
|
||||
QRhiComputePipeline *createComputePipeline() override;
|
||||
|
@ -48,6 +48,8 @@ private slots:
|
||||
void rhiTestData();
|
||||
void create_data();
|
||||
void create();
|
||||
void adapters_data();
|
||||
void adapters();
|
||||
void stats_data();
|
||||
void stats();
|
||||
void nativeHandles_data();
|
||||
@ -443,6 +445,76 @@ void tst_QRhi::create()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QRhi::adapters_data()
|
||||
{
|
||||
rhiTestData();
|
||||
}
|
||||
|
||||
void tst_QRhi::adapters()
|
||||
{
|
||||
QFETCH(QRhi::Implementation, impl);
|
||||
QFETCH(QRhiInitParams *, initParams);
|
||||
|
||||
QRhi::AdapterList adapters = QRhi::enumerateAdapters(impl, initParams);
|
||||
for (QRhiAdapter *adapter : adapters)
|
||||
qDebug() << adapter->info();
|
||||
|
||||
if (!adapters.isEmpty()) {
|
||||
QRhi *rhi = QRhi::create(impl, initParams, QRhi::Flags(), nullptr, adapters[0]);
|
||||
if (rhi) {
|
||||
QCOMPARE(rhi->driverInfo().deviceName, adapters[0]->info().deviceName);
|
||||
QCOMPARE(rhi->driverInfo().deviceId, adapters[0]->info().deviceId);
|
||||
QCOMPARE(rhi->driverInfo().vendorId, adapters[0]->info().vendorId);
|
||||
QCOMPARE(rhi->driverInfo().deviceType, adapters[0]->info().deviceType);
|
||||
}
|
||||
delete rhi;
|
||||
|
||||
// test filtering based on luid/physdev
|
||||
if (impl == QRhi::D3D11) {
|
||||
#ifdef TST_D3D11
|
||||
QRhi *rhi = QRhi::create(impl, initParams);
|
||||
if (rhi) {
|
||||
QRhiD3D11NativeHandles h = *static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles());
|
||||
delete rhi;
|
||||
if (h.adapterLuidLow || h.adapterLuidHigh) {
|
||||
QRhi::AdapterList filteredList = QRhi::enumerateAdapters(impl, initParams, &h);
|
||||
QCOMPARE(filteredList.count(), 1);
|
||||
qDeleteAll(filteredList);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else if (impl == QRhi::D3D12) {
|
||||
#ifdef TST_D3D12
|
||||
QRhi *rhi = QRhi::create(impl, initParams);
|
||||
if (rhi) {
|
||||
QRhiD3D12NativeHandles h = *static_cast<const QRhiD3D12NativeHandles *>(rhi->nativeHandles());
|
||||
delete rhi;
|
||||
if (h.adapterLuidLow || h.adapterLuidHigh) {
|
||||
QRhi::AdapterList filteredList = QRhi::enumerateAdapters(impl, initParams, &h);
|
||||
QCOMPARE(filteredList.count(), 1);
|
||||
qDeleteAll(filteredList);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else if (impl == QRhi::Vulkan) {
|
||||
#ifdef TST_VK
|
||||
QRhi *rhi = QRhi::create(impl, initParams);
|
||||
if (rhi) {
|
||||
QRhiVulkanNativeHandles h = *static_cast<const QRhiVulkanNativeHandles *>(rhi->nativeHandles());
|
||||
delete rhi;
|
||||
QVERIFY(h.physDev);
|
||||
QRhi::AdapterList filteredList = QRhi::enumerateAdapters(impl, initParams, &h);
|
||||
QCOMPARE(filteredList.count(), 1);
|
||||
qDeleteAll(filteredList);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
qDeleteAll(adapters);
|
||||
adapters.clear();
|
||||
}
|
||||
|
||||
void tst_QRhi::stats_data()
|
||||
{
|
||||
rhiTestData();
|
||||
|
@ -224,6 +224,8 @@ bool Window::event(QEvent *e)
|
||||
|
||||
void Window::init()
|
||||
{
|
||||
QRhi::AdapterList adapters;
|
||||
|
||||
if (graphicsApi == Null) {
|
||||
QRhiNullInitParams params;
|
||||
m_r = QRhi::create(QRhi::Null, ¶ms, rhiFlags);
|
||||
@ -244,6 +246,7 @@ void Window::init()
|
||||
QRhiVulkanInitParams params;
|
||||
params.inst = vulkanInstance();
|
||||
params.window = this;
|
||||
adapters = QRhi::enumerateAdapters(QRhi::Vulkan, ¶ms);
|
||||
m_r = QRhi::create(QRhi::Vulkan, ¶ms, rhiFlags);
|
||||
}
|
||||
#endif
|
||||
@ -254,12 +257,14 @@ void Window::init()
|
||||
if (debugLayer)
|
||||
qDebug("Enabling D3D11 debug layer");
|
||||
params.enableDebugLayer = debugLayer;
|
||||
adapters = QRhi::enumerateAdapters(QRhi::D3D11, ¶ms);
|
||||
m_r = QRhi::create(QRhi::D3D11, ¶ms, rhiFlags);
|
||||
} else if (graphicsApi == D3D12) {
|
||||
QRhiD3D12InitParams params;
|
||||
if (debugLayer)
|
||||
qDebug("Enabling D3D12 debug layer");
|
||||
params.enableDebugLayer = debugLayer;
|
||||
adapters = QRhi::enumerateAdapters(QRhi::D3D12, ¶ms);
|
||||
m_r = QRhi::create(QRhi::D3D12, ¶ms, rhiFlags);
|
||||
}
|
||||
#endif
|
||||
@ -271,6 +276,12 @@ void Window::init()
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!adapters.isEmpty()) {
|
||||
qDebug() << "For information, enumerateAdapters() reports:";
|
||||
for (qsizetype i = 0; i < adapters.count(); ++i)
|
||||
qDebug() << " QRhiAdapter #" << i << ":" << adapters[i]->info();
|
||||
}
|
||||
|
||||
if (!m_r)
|
||||
qFatal("Failed to create RHI backend");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user