diff --git a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp
index f29314ce4..f42df5f18 100644
--- a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp
+++ b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp
@@ -654,6 +654,8 @@ void LiveCapture::updateAPIStatus()
if(!m_APIs[api].supported)
{
apiStatus += tr(", %1 (Unsupported)").arg(api);
+ if(!m_APIs[api].supportMessage.isEmpty())
+ apiStatus += lit("\n") + m_APIs[api].supportMessage;
}
else if(!m_APIs[api].presenting)
{
@@ -665,6 +667,8 @@ void LiveCapture::updateAPIStatus()
// remove the redundant starting ", "
apiStatus.remove(0, 2);
+ apiStatus.replace(QLatin1Char('\n'), lit("
"));
+
ui->apiStatus->setText(apiStatus);
ui->apiIcon->setVisible(nonpresenting);
@@ -1344,13 +1348,11 @@ void LiveCapture::connectionThreadEntry()
if(msg.type == TargetControlMessageType::RegisterAPI)
{
- QString api = msg.apiUse.name;
- bool presenting = msg.apiUse.presenting;
- bool supported = msg.apiUse.supported;
- GUIInvoke::call(this, [this, api, presenting, supported]() {
- m_APIs[api] = APIStatus(presenting, supported);
+ GUIInvoke::call(this, [this, msg]() {
+ m_APIs[msg.apiUse.name] =
+ APIStatus(msg.apiUse.presenting, msg.apiUse.supported, msg.apiUse.supportMessage);
- if(presenting && supported)
+ if(msg.apiUse.presenting && msg.apiUse.supported)
{
ui->triggerImmediateCapture->setEnabled(true);
ui->triggerDelayedCapture->setEnabled(true);
diff --git a/qrenderdoc/Windows/Dialogs/LiveCapture.h b/qrenderdoc/Windows/Dialogs/LiveCapture.h
index fa10c8030..89e6a53b5 100644
--- a/qrenderdoc/Windows/Dialogs/LiveCapture.h
+++ b/qrenderdoc/Windows/Dialogs/LiveCapture.h
@@ -127,9 +127,10 @@ private:
struct APIStatus
{
APIStatus() = default;
- APIStatus(bool p, bool s) : presenting(p), supported(s) {}
+ APIStatus(bool p, bool s, rdcstr m) : presenting(p), supported(s), supportMessage(m) {}
bool presenting = false;
bool supported = false;
+ rdcstr supportMessage;
};
Capture *GetCapture(QListWidgetItem *item);
diff --git a/qrenderdoc/Windows/Dialogs/LiveCapture.ui b/qrenderdoc/Windows/Dialogs/LiveCapture.ui
index cc3396a77..785b04e67 100644
--- a/qrenderdoc/Windows/Dialogs/LiveCapture.ui
+++ b/qrenderdoc/Windows/Dialogs/LiveCapture.ui
@@ -109,7 +109,7 @@
API:
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignTop
diff --git a/renderdoc/api/replay/control_types.h b/renderdoc/api/replay/control_types.h
index 9ed872464..541924f92 100644
--- a/renderdoc/api/replay/control_types.h
+++ b/renderdoc/api/replay/control_types.h
@@ -562,6 +562,12 @@ struct APIUseData
DOCUMENT("``True`` if the API can be captured.");
bool supported = false;
+
+ DOCUMENT(R"(A string message if the API is unsupported explaining why.
+
+:type: str
+)");
+ rdcstr supportMessage;
};
DECLARE_REFLECTION_STRUCT(APIUseData);
diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp
index 167caaf9b..4ad37e40c 100644
--- a/renderdoc/core/core.cpp
+++ b/renderdoc/core/core.cpp
@@ -1669,7 +1669,16 @@ void RenderDoc::AddActiveDriver(RDCDriver driver, bool present)
}
}
-std::map RenderDoc::GetActiveDrivers()
+void RenderDoc::SetDriverUnsupportedMessage(RDCDriver driver, rdcstr message)
+{
+ if(driver == RDCDriver::Unknown)
+ return;
+
+ SCOPED_LOCK(m_DriverLock);
+ m_APISupportMessages[driver] = message;
+}
+
+std::map RenderDoc::GetActiveDrivers()
{
std::map drivers;
@@ -1678,20 +1687,28 @@ std::map RenderDoc::GetActiveDrivers()
drivers = m_ActiveDrivers;
}
- std::map ret;
+ std::map ret;
for(auto it = drivers.begin(); it != drivers.end(); ++it)
{
+ RDCDriverStatus &status = ret[it->first];
// driver is presenting if the timestamp is greater than 0 and less than 10 seconds ago (gives a
// little leeway for loading screens or something where the presentation stops temporarily).
// we also assume that during a capture if it was presenting, then it's still capturing.
// Otherwise a long capture would temporarily set it as not presenting.
- bool presenting = it->second > 0;
+ status.presenting = it->second > 0;
- if(presenting && !IsFrameCapturing() && it->second < Timing::GetUnixTimestamp() - 10)
- presenting = false;
+ if(status.presenting && !IsFrameCapturing() && it->second < Timing::GetUnixTimestamp() - 10)
+ status.presenting = false;
- ret[it->first] = presenting;
+ status.supported = (HasRemoteDriver(it->first) || HasReplayDriver(it->first)) &&
+ HasActiveFrameCapturer(it->first);
+
+ if(!status.supported)
+ {
+ SCOPED_LOCK(m_DriverLock);
+ status.supportMessage = m_APISupportMessages[it->first];
+ }
}
return ret;
diff --git a/renderdoc/core/core.h b/renderdoc/core/core.h
index 850783b00..ecd9f8001 100644
--- a/renderdoc/core/core.h
+++ b/renderdoc/core/core.h
@@ -236,6 +236,20 @@ enum class RDCDriver : uint32_t
DECLARE_REFLECTION_ENUM(RDCDriver);
+struct RDCDriverStatus
+{
+ bool presenting = false;
+ bool supported = false;
+ rdcstr supportMessage;
+
+ bool operator==(const RDCDriverStatus &o) const
+ {
+ return presenting == o.presenting && supported == o.supported &&
+ supportMessage == o.supportMessage;
+ }
+ bool operator!=(const RDCDriverStatus &o) const { return !(*this == o); }
+};
+
enum ReplayLogType
{
eReplay_Full,
@@ -547,7 +561,8 @@ public:
bool HasRemoteDriver(RDCDriver driver) const;
void AddActiveDriver(RDCDriver driver, bool present);
- std::map GetActiveDrivers();
+ void SetDriverUnsupportedMessage(RDCDriver driver, rdcstr message);
+ std::map GetActiveDrivers();
uint32_t GetTargetControlIdent() const { return m_RemoteIdent; }
bool IsTargetControlConnected();
@@ -644,6 +659,7 @@ private:
int32_t m_MarkerIndentLevel;
Threading::CriticalSection m_DriverLock;
std::map m_ActiveDrivers;
+ std::map m_APISupportMessages;
Threading::ThreadHandle m_AvailableGPUThread = 0;
rdcarray m_AvailableGPUs;
diff --git a/renderdoc/core/target_control.cpp b/renderdoc/core/target_control.cpp
index 327abbf44..bd16c1816 100644
--- a/renderdoc/core/target_control.cpp
+++ b/renderdoc/core/target_control.cpp
@@ -32,7 +32,7 @@
#include "replay/replay_driver.h"
#include "serialise/serialiser.h"
-static const uint32_t TargetControlProtocolVersion = 7;
+static const uint32_t TargetControlProtocolVersion = 8;
static bool IsProtocolVersionSupported(const uint32_t protocolVersion)
{
@@ -56,6 +56,10 @@ static bool IsProtocolVersionSupported(const uint32_t protocolVersion)
if(protocolVersion == 6)
return true;
+ // 7 -> 8 add custom message for unsupported APIs
+ if(protocolVersion == 7)
+ return true;
+
if(protocolVersion == TargetControlProtocolVersion)
return true;
@@ -156,7 +160,7 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli
rdcarray captures;
rdcarray > children;
- std::map drivers;
+ std::map drivers;
float prevCaptureProgress = captureProgress;
uint32_t prevWindows = 0;
@@ -171,7 +175,7 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli
Threading::Sleep(ticktime);
curtime += ticktime;
- std::map curdrivers = RenderDoc::Inst().GetActiveDrivers();
+ std::map curdrivers = RenderDoc::Inst().GetActiveDrivers();
rdcarray caps = RenderDoc::Inst().GetCaptures();
rdcarray > childprocs = RenderDoc::Inst().GetChildProcesses();
@@ -182,7 +186,7 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli
{
// find the first difference, either a new key or a key with a different value, and send it.
RDCDriver driver = RDCDriver::Unknown;
- bool presenting = false;
+ RDCDriverStatus status;
// search for new drivers
for(auto it = curdrivers.begin(); it != curdrivers.end(); it++)
@@ -190,7 +194,7 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli
if(drivers.find(it->first) == drivers.end() || drivers[it->first] != it->second)
{
driver = it->first;
- presenting = it->second;
+ status = it->second;
break;
}
}
@@ -198,19 +202,18 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli
RDCASSERTNOTEQUAL(driver, RDCDriver::Unknown);
if(driver != RDCDriver::Unknown)
- drivers[driver] = presenting;
-
- bool supported =
- RenderDoc::Inst().HasRemoteDriver(driver) || RenderDoc::Inst().HasReplayDriver(driver);
-
- supported &= RenderDoc::Inst().HasActiveFrameCapturer(driver);
+ drivers[driver] = status;
WRITE_DATA_SCOPE();
{
SCOPED_SERIALISE_CHUNK(ePacket_APIUse);
SERIALISE_ELEMENT(driver);
- SERIALISE_ELEMENT(presenting);
- SERIALISE_ELEMENT(supported);
+ SERIALISE_ELEMENT(status.presenting);
+ SERIALISE_ELEMENT(status.supported);
+ if(version >= 8)
+ {
+ SERIALISE_ELEMENT(status.supportMessage);
+ }
}
}
else if(caps.size() != captures.size())
@@ -843,15 +846,21 @@ public:
RDCDriver driver = RDCDriver::Unknown;
bool presenting = false;
bool supported = false;
+ rdcstr supportMessage;
READ_DATA_SCOPE();
SERIALISE_ELEMENT(driver);
SERIALISE_ELEMENT(presenting);
SERIALISE_ELEMENT(supported);
+ if(m_Version >= 8)
+ {
+ SERIALISE_ELEMENT(supportMessage);
+ }
msg.apiUse.name = ToStr(driver);
msg.apiUse.presenting = presenting;
msg.apiUse.supported = supported;
+ msg.apiUse.supportMessage = supportMessage;
if(presenting)
m_API = ToStr(driver);
@@ -859,6 +868,8 @@ public:
RDCLOG("Used API: %s (%s & %s)", msg.apiUse.name.c_str(),
presenting ? "Presenting" : "Not presenting",
supported ? "supported" : "not supported");
+ if(!supportMessage.empty())
+ RDCLOG("Support: %s", supportMessage.c_str());
reader.EndChunk();
return msg;
diff --git a/renderdoc/driver/d3d8/d3d8_device.cpp b/renderdoc/driver/d3d8/d3d8_device.cpp
index 7effac3c9..ccb9b23d8 100644
--- a/renderdoc/driver/d3d8/d3d8_device.cpp
+++ b/renderdoc/driver/d3d8/d3d8_device.cpp
@@ -283,6 +283,7 @@ HRESULT __stdcall WrappedD3DDevice8::Present(CONST RECT *pSourceRect, CONST RECT
}
RenderDoc::Inst().AddActiveDriver(RDCDriver::D3D8, true);
+ RenderDoc::Inst().SetDriverUnsupportedMessage(RDCDriver::D3D8, "D3D8 is not a supported API");
return m_device->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
}
diff --git a/renderdoc/driver/d3d9/d3d9_device.cpp b/renderdoc/driver/d3d9/d3d9_device.cpp
index ac54065e1..2b392fb2e 100644
--- a/renderdoc/driver/d3d9/d3d9_device.cpp
+++ b/renderdoc/driver/d3d9/d3d9_device.cpp
@@ -257,6 +257,7 @@ HRESULT __stdcall WrappedD3DDevice9::Present(CONST RECT *pSourceRect, CONST RECT
}
RenderDoc::Inst().AddActiveDriver(RDCDriver::D3D9, true);
+ RenderDoc::Inst().SetDriverUnsupportedMessage(RDCDriver::D3D9, "D3D9 is not a supported API");
return m_device->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
}
diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp
index 95d76b15d..950239bc7 100644
--- a/renderdoc/driver/gl/gl_driver.cpp
+++ b/renderdoc/driver/gl/gl_driver.cpp
@@ -1027,7 +1027,28 @@ void WrappedOpenGL::UseUnusedSupportedFunction(const char *name)
}
}
+ size_t sz = m_UnsupportedFunctions.size();
m_UnsupportedFunctions.insert(name);
+
+ if(sz != m_UnsupportedFunctions.size())
+ {
+ RDCERR("Unsupported function %s used", name);
+
+ rdcstr unsupportedStatus = StringFormat::Fmt(
+ "Unsupported %s used:\n", m_UnsupportedFunctions.size() == 1 ? "function" : "functions");
+ size_t i = 0;
+ for(const char *func : m_UnsupportedFunctions)
+ {
+ i++;
+ if(i > 4)
+ break;
+ unsupportedStatus += StringFormat::Fmt(" - %s\n", func);
+ }
+ if(m_UnsupportedFunctions.size() > i)
+ unsupportedStatus += " - ...\n";
+
+ RenderDoc::Inst().SetDriverUnsupportedMessage(RDCDriver::OpenGL, unsupportedStatus);
+ }
}
void WrappedOpenGL::CheckImplicitThread()
@@ -2073,7 +2094,9 @@ void WrappedOpenGL::SwapBuffers(WindowingSystem winSystem, void *windowHandle)
// print the unsupported functions (up to a handful) to show
if(!m_UnsupportedFunctions.empty())
{
- overlayText += "Captures disabled.\nUnsupported function used:\n";
+ overlayText +=
+ StringFormat::Fmt("Captures disabled.\nUnsupported %s used:\n",
+ m_UnsupportedFunctions.size() == 1 ? "function" : "functions");
size_t i = 0;
for(const char *func : m_UnsupportedFunctions)
{