Add a function to enumerate which GPUs are available at replay time

This commit is contained in:
baldurk
2019-08-27 18:51:56 +01:00
parent d81331918e
commit 878acd4ad9
21 changed files with 460 additions and 0 deletions
+2
View File
@@ -314,6 +314,8 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, VertexInputAttribute)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, BoundResource)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, BoundResourceArray)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, FloatVector)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, GraphicsAPI)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, GPUDevice)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, Attachment)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, BindingElement)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, DescriptorBinding)
+38
View File
@@ -660,3 +660,41 @@ structured data.
};
DECLARE_REFLECTION_STRUCT(CaptureFileFormat);
DOCUMENT("Describes a single GPU at replay time.");
struct GPUDevice
{
DOCUMENT("");
GPUDevice() = default;
GPUDevice(const GPUDevice &) = default;
bool operator==(const GPUDevice &o) const
{
// deliberately don't compare name or APIs - only this triple counts for equality
return vendor == o.vendor && deviceID == o.deviceID && driver == o.driver;
}
bool operator<(const GPUDevice &o) const
{
if(!(vendor == o.vendor))
return vendor < o.vendor;
if(!(deviceID == o.deviceID))
return deviceID < o.deviceID;
if(!(driver == o.driver))
return driver < o.driver;
return false;
}
DOCUMENT("The :class:`GPUVendor` of this GPU.");
GPUVendor vendor = GPUVendor::Unknown;
DOCUMENT("The PCI deviceID of this GPU.");
uint32_t deviceID = 0;
DOCUMENT("The name of the driver of this GPU, if multiple drivers are available for it.");
rdcstr driver;
DOCUMENT("The human-readable name of this GPU.");
rdcstr name;
DOCUMENT("The list of APIs that this device supports.");
rdcarray<GraphicsAPI> apis;
};
DECLARE_REFLECTION_STRUCT(GPUDevice);
+8
View File
@@ -1451,6 +1451,14 @@ and construction of files.
)");
struct ICaptureAccess
{
DOCUMENT(R"(Returns the list of available GPUs, that can be used in combination with
:class:`ReplayOptions` to force replay on a particular GPU.
:return: The list of GPUs available.
:rtype: ``list`` of :class:`GPUDevice`
)");
virtual rdcarray<GPUDevice> GetAvailableGPUs() = 0;
DOCUMENT(R"(Retrieve the total number of available sections.
:return: The number of sections in the capture
+112
View File
@@ -1131,6 +1131,118 @@ std::vector<CaptureFileFormat> RenderDoc::GetCaptureFileFormats()
return ret;
}
rdcarray<GPUDevice> RenderDoc::GetAvailableGPUs()
{
if(m_AvailableGPUs.empty())
{
for(GraphicsAPI api : {GraphicsAPI::D3D11, GraphicsAPI::D3D12, GraphicsAPI::Vulkan})
{
RDCDriver driverType = RDCDriver::Unknown;
switch(api)
{
case GraphicsAPI::D3D11: driverType = RDCDriver::D3D11; break;
case GraphicsAPI::D3D12: driverType = RDCDriver::D3D12; break;
case GraphicsAPI::OpenGL: break;
case GraphicsAPI::Vulkan: driverType = RDCDriver::Vulkan; break;
}
if(driverType == RDCDriver::Unknown || !HasReplayDriver(driverType))
continue;
IReplayDriver *driver = NULL;
ReplayStatus status = CreateProxyReplayDriver(driverType, &driver);
if(status == ReplayStatus::Succeeded)
{
rdcarray<GPUDevice> gpus = driver->GetAvailableGPUs();
for(const GPUDevice &newgpu : gpus)
{
bool addnew = true;
for(GPUDevice &oldgpu : m_AvailableGPUs)
{
// if we have this GPU listed already, just add its API to the previous list
if(oldgpu == newgpu)
{
oldgpu.apis.push_back(api);
addnew = false;
}
}
if(addnew)
m_AvailableGPUs.push_back(newgpu);
}
}
else
{
RDCERR("Couldn't create proxy replay driver for %s: %s", ToStr(driverType).c_str(),
ToStr(status).c_str());
}
if(driver)
driver->Shutdown();
}
// we now have a list of GPUs, however we might have some duplicates if some APIs have
// multiple drivers for a single device. To compact this list, for each GPU with no driver
// we find all matching multi-drive GPUs and merge it into all matching copies.
bool hasDriverNames = false;
for(size_t i = 0; i < m_AvailableGPUs.size(); i++)
hasDriverNames |= !m_AvailableGPUs[i].driver.empty();
if(hasDriverNames)
{
for(size_t i = 0; i < m_AvailableGPUs.size();)
{
bool applied = false;
if(!m_AvailableGPUs[i].driver.empty())
{
i++;
continue;
}
// scan all subsequent GPUs, if we find a duplicate, merge the APIs
for(size_t j = i + 1; j < m_AvailableGPUs.size(); j++)
{
if(m_AvailableGPUs[i].vendor == m_AvailableGPUs[j].vendor &&
m_AvailableGPUs[i].deviceID == m_AvailableGPUs[j].deviceID)
{
RDCASSERT(!m_AvailableGPUs[j].driver.empty());
for(GraphicsAPI a : m_AvailableGPUs[i].apis)
{
if(m_AvailableGPUs[j].apis.indexOf(a) == -1)
m_AvailableGPUs[j].apis.push_back(a);
}
applied = true;
}
}
// we "applied" this GPU to all its driver-based duplicates, so we can remove it now
if(applied)
{
m_AvailableGPUs.erase(i);
}
else
{
i++;
}
}
}
// sort the APIs list in each GPU, and sort the GPUs
std::sort(m_AvailableGPUs.begin(), m_AvailableGPUs.end());
for(GPUDevice &dev : m_AvailableGPUs)
{
std::sort(dev.apis.begin(), dev.apis.end());
}
}
return m_AvailableGPUs;
}
bool RenderDoc::HasReplaySupport(RDCDriver driverType)
{
if(driverType == RDCDriver::Image)
+3
View File
@@ -496,6 +496,7 @@ public:
IDeviceProtocolHandler *GetDeviceProtocol(const rdcstr &protocol);
std::vector<CaptureFileFormat> GetCaptureFileFormats();
rdcarray<GPUDevice> GetAvailableGPUs();
void SetVulkanLayerCheck(VulkanLayerCheck callback) { m_VulkanCheck = callback; }
void SetVulkanLayerInstall(VulkanLayerInstall callback) { m_VulkanInstall = callback; }
@@ -635,6 +636,8 @@ private:
Threading::CriticalSection m_DriverLock;
std::map<RDCDriver, uint64_t> m_ActiveDrivers;
rdcarray<GPUDevice> m_AvailableGPUs;
std::map<rdcstr, RENDERDOC_ProgressCallback> m_ProgressCallbacks;
Threading::CriticalSection m_CaptureLock;
+1
View File
@@ -196,6 +196,7 @@ public:
DriverInformation ret = {};
return ret;
}
rdcarray<GPUDevice> GetAvailableGPUs() { return {}; }
const D3D12Pipe::State *GetD3D12PipelineState() { return NULL; }
const GLPipe::State *GetGLPipelineState() { return NULL; }
const VKPipe::State *GetVulkanPipelineState() { return NULL; }
+44
View File
@@ -71,6 +71,7 @@ enum RemoteServerPacket
eRemoteServer_GetSectionProperties,
eRemoteServer_GetSectionContents,
eRemoteServer_WriteSection,
eRemoteServer_GetAvailableGPUs,
eRemoteServer_RemoteServerCount,
};
@@ -367,6 +368,18 @@ static void ActiveRemoteClientThread(ClientThread *threadData,
tempFiles.push_back(path);
}
else if(type == eRemoteServer_GetAvailableGPUs)
{
reader.EndChunk();
rdcarray<GPUDevice> gpus = RenderDoc::Inst().GetAvailableGPUs();
{
WRITE_DATA_SCOPE();
SCOPED_SERIALISE_CHUNK(eRemoteServer_GetAvailableGPUs);
SERIALISE_ELEMENT(gpus);
}
}
else if(type == eRemoteServer_ShutdownServer)
{
reader.EndChunk();
@@ -1568,6 +1581,37 @@ rdcstr RemoteServer::DriverName()
return driverName;
}
rdcarray<GPUDevice> RemoteServer::GetAvailableGPUs()
{
if(!Connected())
return {};
{
WRITE_DATA_SCOPE();
SCOPED_SERIALISE_CHUNK(eRemoteServer_GetAvailableGPUs);
}
rdcarray<GPUDevice> gpus;
{
READ_DATA_SCOPE();
RemoteServerPacket type = ser.ReadChunk<RemoteServerPacket>();
if(type == eRemoteServer_GetAvailableGPUs)
{
SERIALISE_ELEMENT(gpus);
}
else
{
RDCERR("Unexpected response to GetAvailableGPUs");
}
ser.EndChunk();
}
return gpus;
}
int RemoteServer::GetSectionCount()
{
if(!Connected())
+2
View File
@@ -77,6 +77,8 @@ public:
virtual rdcstr DriverName();
virtual rdcarray<GPUDevice> GetAvailableGPUs();
virtual int GetSectionCount();
virtual int FindSectionByName(const char *name);
+30
View File
@@ -324,6 +324,35 @@ DriverInformation ReplayProxy::GetDriverInfo()
PROXY_FUNCTION(GetDriverInfo);
}
template <typename ParamSerialiser, typename ReturnSerialiser>
rdcarray<GPUDevice> ReplayProxy::Proxied_GetAvailableGPUs(ParamSerialiser &paramser,
ReturnSerialiser &retser)
{
const ReplayProxyPacket expectedPacket = eReplayProxy_GetAvailableGPUs;
ReplayProxyPacket packet = eReplayProxy_GetAvailableGPUs;
rdcarray<GPUDevice> ret = {};
{
BEGIN_PARAMS();
END_PARAMS();
}
{
REMOTE_EXECUTION();
if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored)
ret = m_Remote->GetAvailableGPUs();
}
SERIALISE_RETURN(ret);
return ret;
}
rdcarray<GPUDevice> ReplayProxy::GetAvailableGPUs()
{
PROXY_FUNCTION(GetAvailableGPUs);
}
template <typename ParamSerialiser, typename ReturnSerialiser>
std::vector<DebugMessage> ReplayProxy::Proxied_GetDebugMessages(ParamSerialiser &paramser,
ReturnSerialiser &retser)
@@ -2663,6 +2692,7 @@ bool ReplayProxy::Tick(int type)
case eReplayProxy_GetDisassemblyTargets: GetDisassemblyTargets(); break;
case eReplayProxy_GetTargetShaderEncodings: GetTargetShaderEncodings(); break;
case eReplayProxy_GetDriverInfo: GetDriverInfo(); break;
case eReplayProxy_GetAvailableGPUs: GetAvailableGPUs(); break;
default: RDCERR("Unexpected command %u", type); return false;
}
+2
View File
@@ -99,6 +99,7 @@ enum ReplayProxyPacket
eReplayProxy_GetTargetShaderEncodings,
eReplayProxy_GetDriverInfo,
eReplayProxy_GetAvailableGPUs,
};
DECLARE_REFLECTION_ENUM(ReplayProxyPacket);
@@ -470,6 +471,7 @@ public:
IMPLEMENT_FUNCTION_PROXIED(APIProperties, GetAPIProperties);
IMPLEMENT_FUNCTION_PROXIED(DriverInformation, GetDriverInfo);
IMPLEMENT_FUNCTION_PROXIED(rdcarray<GPUDevice>, GetAvailableGPUs);
IMPLEMENT_FUNCTION_PROXIED(std::vector<DebugMessage>, GetDebugMessages);
+44
View File
@@ -515,6 +515,50 @@ std::vector<DebugMessage> D3D11Replay::GetDebugMessages()
return m_pDevice->GetDebugMessages();
}
rdcarray<GPUDevice> D3D11Replay::GetAvailableGPUs()
{
rdcarray<GPUDevice> ret;
for(UINT i = 0; i < 10; i++)
{
IDXGIAdapter *adapter = NULL;
HRESULT hr = m_pFactory->EnumAdapters(i, &adapter);
if(SUCCEEDED(hr) && adapter)
{
DXGI_ADAPTER_DESC desc;
adapter->GetDesc(&desc);
GPUDevice dev;
dev.vendor = GPUVendorFromPCIVendor(desc.VendorId);
dev.deviceID = desc.DeviceId;
dev.driver = ""; // D3D doesn't have multiple drivers per API
dev.name = StringFormat::Wide2UTF8(desc.Description);
dev.apis = {GraphicsAPI::D3D11};
// don't add duplicate devices even if they get enumerated. Don't add WARP, we'll do that
// manually since it's inconsistently enumerated
if(ret.indexOf(dev) == -1 && dev.vendor != GPUVendor::Software)
ret.push_back(dev);
}
SAFE_RELEASE(adapter);
}
{
GPUDevice dev;
dev.vendor = GPUVendor::Software;
dev.deviceID = 0;
dev.driver = ""; // D3D doesn't have multiple drivers per API
dev.name = "WARP Rasterizer";
dev.apis = {GraphicsAPI::D3D11};
ret.push_back(dev);
}
return ret;
}
APIProperties D3D11Replay::GetAPIProperties()
{
APIProperties ret = m_pDevice->APIProps;
+1
View File
@@ -105,6 +105,7 @@ public:
void DestroyResources();
DriverInformation GetDriverInfo() { return m_DriverInfo; }
rdcarray<GPUDevice> GetAvailableGPUs();
APIProperties GetAPIProperties();
ResourceDescription &GetResourceDesc(ResourceId id);
+46
View File
@@ -214,6 +214,52 @@ ReplayStatus D3D12Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructur
return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers);
}
rdcarray<GPUDevice> D3D12Replay::GetAvailableGPUs()
{
rdcarray<GPUDevice> ret;
for(UINT i = 0; i < 10; i++)
{
IDXGIAdapter *adapter = NULL;
HRESULT hr = m_pFactory->EnumAdapters(i, &adapter);
if(SUCCEEDED(hr) && adapter)
{
DXGI_ADAPTER_DESC desc;
adapter->GetDesc(&desc);
GPUDevice dev;
dev.vendor = GPUVendorFromPCIVendor(desc.VendorId);
dev.deviceID = desc.DeviceId;
dev.driver = ""; // D3D doesn't have multiple drivers per API
dev.name = StringFormat::Wide2UTF8(desc.Description);
dev.apis = {GraphicsAPI::D3D12};
// don't add duplicate devices even if they get enumerated. Don't add WARP, we'll do that
// manually since it's inconsistently enumerated
if(ret.indexOf(dev) == -1 && dev.vendor != GPUVendor::Software)
ret.push_back(dev);
}
SAFE_RELEASE(adapter);
}
// add WARP as long as we're not 12On7 (where we can't use WARP)
if(!m_D3D12On7)
{
GPUDevice dev;
dev.vendor = GPUVendor::Software;
dev.deviceID = 0;
dev.driver = ""; // D3D doesn't have multiple drivers per API
dev.name = "WARP Rasterizer";
dev.apis = {GraphicsAPI::D3D12};
ret.push_back(dev);
}
return ret;
}
APIProperties D3D12Replay::GetAPIProperties()
{
APIProperties ret = m_pDevice->APIProps;
+1
View File
@@ -65,6 +65,7 @@ public:
void CreateResources();
void DestroyResources();
DriverInformation GetDriverInfo() { return m_DriverInfo; }
rdcarray<GPUDevice> GetAvailableGPUs();
APIProperties GetAPIProperties();
ResourceDescription &GetResourceDesc(ResourceId id);
+6
View File
@@ -167,6 +167,12 @@ ResourceId GLReplay::GetLiveID(ResourceId id)
return m_pDriver->GetResourceManager()->GetLiveID(id);
}
rdcarray<GPUDevice> GLReplay::GetAvailableGPUs()
{
// GL doesn't support multiple GPUs, return an empty list
return {};
}
APIProperties GLReplay::GetAPIProperties()
{
APIProperties ret = m_pDriver->APIProps;
+1
View File
@@ -91,6 +91,7 @@ public:
void SetDriver(WrappedOpenGL *d) { m_pDriver = d; }
DriverInformation GetDriverInfo() { return m_DriverInfo; }
rdcarray<GPUDevice> GetAvailableGPUs();
APIProperties GetAPIProperties();
ResourceDescription &GetResourceDesc(ResourceId id);
+102
View File
@@ -80,6 +80,108 @@ void VulkanReplay::Shutdown()
delete m_pDriver;
}
rdcarray<GPUDevice> VulkanReplay::GetAvailableGPUs()
{
rdcarray<GPUDevice> ret;
// do a manual enumerate to avoid any possible remapping
VkInstance instance = m_pDriver->GetInstance();
uint32_t count = 0;
VkResult vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, NULL);
if(vkr != VK_SUCCESS)
return ret;
VkPhysicalDevice *devices = new VkPhysicalDevice[count];
vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, devices);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
for(uint32_t p = 0; p < count; p++)
{
VkPhysicalDeviceProperties props = {};
ObjDisp(instance)->GetPhysicalDeviceProperties(devices[p], &props);
VkPhysicalDeviceDriverPropertiesKHR driverProps = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
};
VkPhysicalDeviceProperties2 physProps2 = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &driverProps,
};
// get driver properties if available
if(m_pDriver->GetExtensions(GetRecord(instance)).ext_KHR_get_physical_device_properties2)
{
uint32_t extCount = 0;
ObjDisp(instance)->EnumerateDeviceExtensionProperties(devices[p], NULL, &extCount, NULL);
VkExtensionProperties *extProps = new VkExtensionProperties[extCount];
ObjDisp(instance)->EnumerateDeviceExtensionProperties(devices[p], NULL, &extCount, extProps);
for(uint32_t e = 0; e < extCount; e++)
{
if(!strcmp(extProps[e].extensionName, VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME))
{
ObjDisp(instance)->GetPhysicalDeviceProperties2(devices[p], &physProps2);
break;
}
}
SAFE_DELETE_ARRAY(extProps);
}
GPUDevice dev;
dev.vendor = GPUVendorFromPCIVendor(props.vendorID);
dev.deviceID = props.deviceID;
dev.name = props.deviceName;
dev.apis = {GraphicsAPI::Vulkan};
// only set the driver name when it's useful to disambiguate
switch(driverProps.driverID)
{
default: dev.driver = "";
case VK_DRIVER_ID_AMD_PROPRIETARY_KHR: dev.driver = "AMD Propriertary"; break;
case VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR: dev.driver = "AMD Open-source"; break;
case VK_DRIVER_ID_MESA_RADV_KHR: dev.driver = "AMD RADV"; break;
case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR: dev.driver = "Intel Propriertary"; break;
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR: dev.driver = "Intel Open-source"; break;
}
// don't add duplicate devices even if they get enumerated.
if(ret.indexOf(dev) == -1)
ret.push_back(dev);
}
// loop over devices and remove the driver string unless it's needed to disambiguate from another
// identical device.
for(size_t i = 0; i < ret.size(); i++)
{
bool needDriver = false;
for(size_t j = 0; j < ret.size(); j++)
{
if(i == j)
continue;
if(ret[i].vendor == ret[j].vendor && ret[i].deviceID == ret[j].deviceID)
{
RDCASSERT(ret[i].driver != ret[j].driver);
needDriver = true;
break;
}
}
if(!needDriver)
ret[i].driver = rdcstr();
}
SAFE_DELETE_ARRAY(devices);
return ret;
}
APIProperties VulkanReplay::GetAPIProperties()
{
APIProperties ret = m_pDriver->APIProps;
+1
View File
@@ -231,6 +231,7 @@ public:
void SetDriver(WrappedVulkan *d) { m_pDriver = d; }
DriverInformation GetDriverInfo() { return m_DriverInfo; }
rdcarray<GPUDevice> GetAvailableGPUs();
APIProperties GetAPIProperties();
ResourceDescription &GetResourceDesc(ResourceId id);
+1
View File
@@ -135,6 +135,7 @@ public:
return RenderDoc::Inst().GetCaptureFileFormats();
}
rdcarray<GPUDevice> GetAvailableGPUs() { return RenderDoc::Inst().GetAvailableGPUs(); }
const SDFile &GetStructuredData()
{
// decompile to structured data on demand.
+13
View File
@@ -879,6 +879,18 @@ void DoSerialise(SerialiserType &ser, CounterValue &el)
SIZE_CHECK(8);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, GPUDevice &el)
{
SERIALISE_MEMBER(vendor);
SERIALISE_MEMBER(deviceID);
SERIALISE_MEMBER(driver);
SERIALISE_MEMBER(name);
SERIALISE_MEMBER(apis);
SIZE_CHECK(80);
}
#pragma region Common pipeline state
template <typename SerialiserType>
@@ -2243,6 +2255,7 @@ INSTANTIATE_SERIALISE_TYPE(PixelModification)
INSTANTIATE_SERIALISE_TYPE(EventUsage)
INSTANTIATE_SERIALISE_TYPE(CounterResult)
INSTANTIATE_SERIALISE_TYPE(CounterValue)
INSTANTIATE_SERIALISE_TYPE(GPUDevice)
INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::Layout)
INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::InputAssembly)
INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::View)
+2
View File
@@ -176,6 +176,8 @@ public:
virtual bool NeedRemapForFetch(const ResourceFormat &format) = 0;
virtual DriverInformation GetDriverInfo() = 0;
virtual rdcarray<GPUDevice> GetAvailableGPUs() = 0;
};
class IReplayDriver : public IRemoteDriver