static QString GetDescription(const EnvironmentModification &env) { QString ret; if(env.mod == EnvMod::Append) ret = QFormatStr("Append %1 with %2 using %3").arg(env.name).arg(env.value).arg(ToQStr(env.sep)); else if(env.mod == EnvMod::Prepend) ret = QFormatStr("Prepend %1 with %2 using %3").arg(env.name).arg(env.value).arg(ToQStr(env.sep)); else ret = QFormatStr("Set %1 to %2").arg(env.name).arg(env.value); return ret; }
QString D3DSemanticString(const SigParameter &sig) { if(sig.systemValue == eAttr_None) return ToQStr(sig.semanticIdxName); QString ret = ToQStr(sig.systemValue); // need to include the index if it's a system value semantic that's numbered if(sig.systemValue == eAttr_ColourOutput || sig.systemValue == eAttr_CullDistance || sig.systemValue == eAttr_ClipDistance) ret += QString::number(sig.semanticIndex); return ret; }
void CaptureContext::SaveRenames() { QVariantMap resources; for(ResourceId id : m_CustomNames.keys()) { resources[ToQStr(id)] = m_CustomNames[id]; } QVariantMap root; root[lit("CustomResourceNames")] = resources; QString json = VariantToJSON(root); SectionProperties props; props.type = SectionType::ResourceRenames; props.version = 1; Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()); }
void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString &origFilename, bool temporary, bool local) { QFileInfo fi(ConfigFile("UI.config")); m_LogFile = origFilename; m_LogLocal = local; m_LoadInProgress = true; if(fi.exists()) Config.Serialize(fi.absoluteFilePath()); float loadProgress = 0.0f; float postloadProgress = 0.0f; QSemaphore progressThread(1); LambdaThread progressTickerThread([this, &progressThread, &loadProgress, &postloadProgress]() { while(progressThread.available()) { QThread::msleep(30); float val = 0.8f * loadProgress + 0.19f * postloadProgress + 0.01f; GUIInvoke::call([this, val]() { m_Progress->setValue(val * 1000); m_MainWindow->setProgress(val); }); } GUIInvoke::call([this]() { m_Progress->setValue(1000); }); }); progressTickerThread.start(); // this function call will block until the log is either loaded, or there's some failure m_Renderer.OpenCapture(logFile, &loadProgress); // if the renderer isn't running, we hit a failure case so display an error message if(!m_Renderer.IsRunning()) { QString errmsg = "Unknown error message"; errmsg = ToQStr(m_Renderer.GetCreateStatus()); progressThread.acquire(); progressTickerThread.wait(); RDDialog::critical(NULL, "Error opening log", QString("%1\nFailed to open logfile for replay: %2.\n\n" "Check diagnostic log in Help menu for more details.") .arg(logFile) .arg(errmsg)); GUIInvoke::call([this]() { m_Progress->setValue(1000); m_MainWindow->setProgress(-1.0f); m_Progress->hide(); }); m_LoadInProgress = false; return; } if(!temporary) { PersistantConfig::AddRecentFile(Config.RecentLogFiles, origFilename, 10); if(fi.exists()) Config.Serialize(fi.absoluteFilePath()); } m_EventID = 0; // fetch initial data like drawcalls, textures and buffers m_Renderer.BlockInvoke([this, &postloadProgress](IReplayRenderer *r) { r->GetFrameInfo(&m_FrameInfo); m_APIProps = r->GetAPIProperties(); postloadProgress = 0.2f; r->GetDrawcalls(&m_Drawcalls); postloadProgress = 0.4f; r->GetSupportedWindowSystems(&m_WinSystems); #if defined(RENDERDOC_PLATFORM_WIN32) m_CurWinSystem = eWindowingSystem_Win32; #elif defined(RENDERDOC_PLATFORM_LINUX) m_CurWinSystem = eWindowingSystem_Xlib; // prefer XCB, if supported for(WindowingSystem sys : m_WinSystems) { if(sys == eWindowingSystem_XCB) { m_CurWinSystem = eWindowingSystem_XCB; break; } } if(m_CurWinSystem == eWindowingSystem_XCB) m_XCBConnection = QX11Info::connection(); else m_X11Display = QX11Info::display(); #endif r->GetBuffers(&m_BufferList); for(FetchBuffer &b : m_BufferList) m_Buffers[b.ID] = &b; postloadProgress = 0.8f; r->GetTextures(&m_TextureList); for(FetchTexture &t : m_TextureList) m_Textures[t.ID] = &t; postloadProgress = 0.9f; r->GetD3D11PipelineState(&CurD3D11PipelineState); r->GetD3D12PipelineState(&CurD3D12PipelineState); r->GetGLPipelineState(&CurGLPipelineState); r->GetVulkanPipelineState(&CurVulkanPipelineState); CurPipelineState.SetStates(m_APIProps, &CurD3D11PipelineState, &CurD3D12PipelineState, &CurGLPipelineState, &CurVulkanPipelineState); UnreadMessageCount = 0; AddMessages(m_FrameInfo.debugMessages); postloadProgress = 1.0f; }); QThread::msleep(20); QDateTime today = QDateTime::currentDateTimeUtc(); QDateTime compare = today.addDays(-21); if(compare > Config.DegradedLog_LastUpdate && m_APIProps.degraded) { Config.DegradedLog_LastUpdate = today; RDDialog::critical( NULL, "Degraded support of log", QString( "%1\nThis log opened with degraded support - " "this could mean missing hardware support caused a fallback to software rendering.\n\n" "This warning will not appear every time this happens, " "check debug errors/warnings window for more details.") .arg(origFilename)); } m_LogLoaded = true; progressThread.acquire(); progressTickerThread.wait(); QVector<ILogViewerForm *> logviewers(m_LogViewers); GUIInvoke::blockcall([&logviewers]() { // notify all the registers log viewers that a log has been loaded for(ILogViewerForm *logviewer : logviewers) { if(logviewer) logviewer->OnLogfileLoaded(); } }); m_LoadInProgress = false; GUIInvoke::call([this]() { m_Progress->setValue(1000); m_MainWindow->setProgress(1.0f); m_Progress->hide(); }); }
void RemoteManager::on_connect_clicked() { RDTreeWidgetItem *node = ui->hosts->selectedItem(); if(!node) return; RemoteConnect connect = getRemoteConnect(node); RemoteHost *host = getRemoteHost(node); if(connect.ident > 0) { connectToApp(node); } else if(host) { if(host->serverRunning) { QMessageBox::StandardButton res = RDDialog::question( this, tr("Remote server shutdown"), tr("Are you sure you wish to shut down running remote server on %1?").arg(host->Name()), RDDialog::YesNoCancel); if(res == QMessageBox::Cancel || res == QMessageBox::No) return; // shut down if(host->connected) { m_Ctx.Replay().ShutdownServer(); setRemoteServerLive(node, false, false); } else { IRemoteServer *server = NULL; ReplayStatus status = RENDERDOC_CreateRemoteServerConnection(host->hostname.c_str(), 0, &server); if(server) server->ShutdownServerAndConnection(); setRemoteServerLive(node, false, false); if(status != ReplayStatus::Succeeded) RDDialog::critical(this, tr("Shutdown error"), tr("Error shutting down remote server: %1").arg(ToQStr(status))); } updateConnectButton(); } else { // try to run ui->refreshOne->setEnabled(false); ui->refreshAll->setEnabled(false); m_Lookups.release(); LambdaThread *th = new LambdaThread([this, node]() { runRemoteServer(node); }); th->selfDelete(true); th->start(); updateLookupsStatus(); } } }
void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const QString &origFilename, bool temporary, bool local) { m_CaptureFile = origFilename; m_CaptureLocal = local; Config().Save(); m_LoadProgress = 0.0f; m_PostloadProgress = 0.0f; // this function call will block until the capture is either loaded, or there's some failure m_Renderer.OpenCapture(captureFile, [this](float p) { m_LoadProgress = p; }); // if the renderer isn't running, we hit a failure case so display an error message if(!m_Renderer.IsRunning()) { QString errmsg = ToQStr(m_Renderer.GetCreateStatus()); QString messageText = tr("%1\nFailed to open capture for replay: %2.\n\n" "Check diagnostic log in Help menu for more details.") .arg(captureFile) .arg(errmsg); RDDialog::critical(NULL, tr("Error opening capture"), messageText); m_LoadInProgress = false; return; } if(!temporary) { AddRecentFile(Config().RecentCaptureFiles, origFilename, 10); Config().Save(); } m_EventID = 0; m_FirstDrawcall = m_LastDrawcall = NULL; // fetch initial data like drawcalls, textures and buffers m_Renderer.BlockInvoke([this](IReplayController *r) { m_FrameInfo = r->GetFrameInfo(); m_APIProps = r->GetAPIProperties(); m_PostloadProgress = 0.2f; m_Drawcalls = r->GetDrawcalls(); AddFakeProfileMarkers(); m_FirstDrawcall = &m_Drawcalls[0]; while(!m_FirstDrawcall->children.empty()) m_FirstDrawcall = &m_FirstDrawcall->children[0]; m_LastDrawcall = &m_Drawcalls.back(); while(!m_LastDrawcall->children.empty()) m_LastDrawcall = &m_LastDrawcall->children.back(); m_PostloadProgress = 0.4f; m_WinSystems = r->GetSupportedWindowSystems(); #if defined(RENDERDOC_PLATFORM_WIN32) m_CurWinSystem = WindowingSystem::Win32; #elif defined(RENDERDOC_PLATFORM_LINUX) m_CurWinSystem = WindowingSystem::Xlib; // prefer XCB, if supported for(WindowingSystem sys : m_WinSystems) { if(sys == WindowingSystem::XCB) { m_CurWinSystem = WindowingSystem::XCB; break; } } if(m_CurWinSystem == WindowingSystem::XCB) m_XCBConnection = QX11Info::connection(); else m_X11Display = QX11Info::display(); #endif m_StructuredFile = &r->GetStructuredFile(); m_ResourceList = r->GetResources(); for(ResourceDescription &res : m_ResourceList) m_Resources[res.resourceId] = &res; m_BufferList = r->GetBuffers(); for(BufferDescription &b : m_BufferList) m_Buffers[b.resourceId] = &b; m_PostloadProgress = 0.8f; m_TextureList = r->GetTextures(); for(TextureDescription &t : m_TextureList) m_Textures[t.resourceId] = &t; m_PostloadProgress = 0.9f; m_CurD3D11PipelineState = &r->GetD3D11PipelineState(); m_CurD3D12PipelineState = &r->GetD3D12PipelineState(); m_CurGLPipelineState = &r->GetGLPipelineState(); m_CurVulkanPipelineState = &r->GetVulkanPipelineState(); m_CurPipelineState.SetStates(m_APIProps, m_CurD3D11PipelineState, m_CurD3D12PipelineState, m_CurGLPipelineState, m_CurVulkanPipelineState); m_UnreadMessageCount = 0; AddMessages(m_FrameInfo.debugMessages); m_PostloadProgress = 1.0f; }); QThread::msleep(20); QDateTime today = QDateTime::currentDateTimeUtc(); QDateTime compare = today.addDays(-21); if(compare > Config().DegradedCapture_LastUpdate && m_APIProps.degraded) { Config().DegradedCapture_LastUpdate = today; RDDialog::critical( NULL, tr("Degraded support of capture"), tr("%1\nThis capture opened with degraded support - " "this could mean missing hardware support caused a fallback to software rendering.\n\n" "This warning will not appear every time this happens, " "check debug errors/warnings window for more details.") .arg(origFilename)); } ICaptureAccess *access = Replay().GetCaptureAccess(); if(access) { int idx = access->FindSectionByType(SectionType::ResourceRenames); if(idx >= 0) { bytebuf buf = access->GetSectionContents(idx); LoadRenames(QString::fromUtf8((const char *)buf.data(), buf.count())); } idx = access->FindSectionByType(SectionType::Bookmarks); if(idx >= 0) { bytebuf buf = access->GetSectionContents(idx); LoadBookmarks(QString::fromUtf8((const char *)buf.data(), buf.count())); } idx = access->FindSectionByType(SectionType::Notes); if(idx >= 0) { bytebuf buf = access->GetSectionContents(idx); LoadNotes(QString::fromUtf8((const char *)buf.data(), buf.count())); } } m_LoadInProgress = false; m_CaptureLoaded = true; }
void AppendUpdateStatistics(QString &statisticsLog, const FetchFrameInfo &frameInfo) { // #mivance see AppendConstantBindStatistics const FetchFrameUpdateStats &reference = frameInfo.stats.updates; FetchFrameUpdateStats totalUpdates; memset(&totalUpdates, 0, sizeof(totalUpdates)); totalUpdates.types.create(reference.types.count); totalUpdates.sizes.create(reference.sizes.count); { FetchFrameUpdateStats updates = frameInfo.stats.updates; totalUpdates.calls += updates.calls; totalUpdates.clients += updates.clients; totalUpdates.servers += updates.servers; for(int t = 0; t < updates.types.count; t++) totalUpdates.types[t] += updates.types[t]; for(int t = 0; t < updates.sizes.count; t++) totalUpdates.sizes[t] += updates.sizes[t]; } statisticsLog.append("\n*** Resource Update Statistics ***\n\n"); statisticsLog.append( QString("Total calls: %1, client-updated memory: %2, server-updated memory: %3\n") .arg(totalUpdates.calls) .arg(totalUpdates.clients) .arg(totalUpdates.servers)); statisticsLog.append("\nUpdated resource types:\n"); uint32_t maxCount = 0; int maxWithValue = 0; for(int s = 1; s < totalUpdates.types.count; s++) { uint32_t value = totalUpdates.types[s]; if(value > 0) maxWithValue = s; maxCount = qMax(maxCount, value); } for(int s = 1; s <= maxWithValue; s++) { uint32_t count = totalUpdates.types[s]; int slice = SliceForString(Stars, count, maxCount); ShaderResourceType type = (ShaderResourceType)s; statisticsLog.append( QString("%1: %2 %3\n").arg(ToQStr(type), 20).arg(Stars.left(slice)).arg(CountOrEmpty(count))); } statisticsLog.append("\nUpdated resource sizes:\n"); maxCount = 0; maxWithValue = 0; for(int s = 0; s < totalUpdates.sizes.count; s++) { uint32_t value = totalUpdates.sizes[s]; if(value > 0) maxWithValue = s; maxCount = qMax(maxCount, value); } for(int s = 0; s <= maxWithValue; s++) { uint32_t count = totalUpdates.sizes[s]; int slice = SliceForString(Stars, count, maxCount); statisticsLog.append(QString("%1: %2 %3\n") .arg(Pow2IndexAsReadable(s), 8) .arg(Stars.left(slice)) .arg(CountOrEmpty(count))); } }
void AppendResourceBindStatistics(CaptureContext *ctx, QString &statisticsLog, const FetchFrameInfo &frameInfo) { // #mivance see AppendConstantBindStatistics const FetchFrameResourceBindStats &reference = frameInfo.stats.resources[0]; FetchFrameResourceBindStats totalResourcesPerStage[eShaderStage_Count]; memset(&totalResourcesPerStage, 0, sizeof(totalResourcesPerStage)); for(int s = eShaderStage_First; s < eShaderStage_Count; s++) { totalResourcesPerStage[s].types.create(reference.types.count); totalResourcesPerStage[s].bindslots.create(reference.bindslots.count); } { const FetchFrameResourceBindStats *resources = frameInfo.stats.resources; for(int s = eShaderStage_First; s < eShaderStage_Count; s++) { totalResourcesPerStage[s].calls += resources[s].calls; totalResourcesPerStage[s].sets += resources[s].sets; totalResourcesPerStage[s].nulls += resources[s].nulls; for(int z = 0; z < resources[s].types.count; z++) { totalResourcesPerStage[s].types[z] += resources[s].types[z]; } for(int l = 0; l < resources[s].bindslots.count; l++) { totalResourcesPerStage[s].bindslots[l] += resources[s].bindslots[l]; } } } FetchFrameResourceBindStats totalResourcesForAllStages; memset(&totalResourcesForAllStages, 0, sizeof(totalResourcesForAllStages)); totalResourcesForAllStages.types.create(totalResourcesPerStage[0].types.count); totalResourcesForAllStages.bindslots.create(totalResourcesPerStage[0].bindslots.count); for(int s = eShaderStage_First; s < eShaderStage_Count; s++) { FetchFrameResourceBindStats perStage = totalResourcesPerStage[s]; totalResourcesForAllStages.calls += perStage.calls; totalResourcesForAllStages.sets += perStage.sets; totalResourcesForAllStages.nulls += perStage.nulls; for(int t = 0; t < perStage.types.count; t++) { totalResourcesForAllStages.types[t] += perStage.types[t]; } for(int l = 0; l < perStage.bindslots.count; l++) { totalResourcesForAllStages.bindslots[l] += perStage.bindslots[l]; } } statisticsLog.append("\n*** Resource Bind Statistics ***\n\n"); for(int s = eShaderStage_First; s < eShaderStage_Count; s++) { statisticsLog.append(QString("%1 calls: %2 non-null resource sets: %3 null resource sets: %4\n") .arg(ctx->CurPipelineState.Abbrev((ShaderStageType)s)) .arg(totalResourcesPerStage[s].calls) .arg(totalResourcesPerStage[s].sets) .arg(totalResourcesPerStage[s].nulls)); } statisticsLog.append( QString("Total calls: %1 non-null resource sets: %2 null resource sets: %3\n") .arg(totalResourcesForAllStages.calls) .arg(totalResourcesForAllStages.sets) .arg(totalResourcesForAllStages.nulls)); uint32_t maxCount = 0; int maxWithCount = 0; statisticsLog.append("\nResource types across all stages:\n"); for(int s = 0; s < totalResourcesForAllStages.types.count; s++) { uint32_t count = totalResourcesForAllStages.types[s]; if(count > 0) maxWithCount = s; maxCount = qMax(maxCount, count); } for(int s = 0; s <= maxWithCount; s++) { uint32_t count = totalResourcesForAllStages.types[s]; int slice = SliceForString(Stars, count, maxCount); ShaderResourceType type = (ShaderResourceType)s; statisticsLog.append( QString("%1: %2 %3\n").arg(ToQStr(type), 20).arg(Stars.left(slice)).arg(CountOrEmpty(count))); } statisticsLog.append(CreateSimpleIntegerHistogram( "Aggregate slot counts per invocation across all stages", totalResourcesForAllStages.bindslots)); }
bool PersistantConfig::Load(const rdcstr &filename) { bool ret = Deserialize(filename); // perform some sanitisation to make sure config is always in sensible state for(const rdcstrpair &key : ConfigSettings) { // redundantly set each setting so it is flushed to the core dll SetConfigSetting(key.first, key.second); } RENDERDOC_SetConfigSetting("Disassembly_FriendlyNaming", ShaderViewer_FriendlyNaming ? "1" : "0"); // localhost should always be available as a remote host bool foundLocalhost = false; for(RemoteHost host : RemoteHostList) { if(host.hostname.isEmpty()) continue; RemoteHosts.push_back(new RemoteHost(host)); if(host.IsLocalhost()) foundLocalhost = true; } if(!foundLocalhost) { RemoteHost *host = new RemoteHost(); host->hostname = "localhost"; RemoteHosts.insert(0, host); } bool tools[arraydim<KnownSPIRVTool>()] = {}; // see which known tools are registered for(const SPIRVDisassembler &dis : SPIRVDisassemblers) { // if it's declared if(dis.tool != KnownSPIRVTool::Unknown) tools[(size_t)dis.tool] = true; for(KnownSPIRVTool tool : values<KnownSPIRVTool>()) { if(QString(dis.executable).contains(ToolExecutable(tool))) tools[(size_t)tool] = true; } } for(KnownSPIRVTool tool : values<KnownSPIRVTool>()) { if(tool == KnownSPIRVTool::Unknown || tools[(size_t)tool]) continue; QString exe = ToolExecutable(tool); if(exe.isEmpty()) continue; // try to find the tool in PATH QString path = QStandardPaths::findExecutable(exe); if(!path.isEmpty()) { SPIRVDisassembler dis; dis.name = ToQStr(tool); // we store just the base name, so when we launch the process it will always find it in PATH, // rather than baking in the current PATH result. dis.executable = exe; dis.tool = tool; SPIRVDisassemblers.push_back(dis); continue; } // try to find it in our plugins folder QDir appDir(QApplication::applicationDirPath()); QStringList searchPaths = {appDir.absoluteFilePath(lit("plugins/spirv/"))}; #if defined(Q_OS_WIN64) searchPaths << appDir.absoluteFilePath(lit("../../plugins-win64/spirv/")); #elif defined(Q_OS_WIN64) searchPaths << appDir.absoluteFilePath(lit("../../plugins-win32/spirv/")); #elif defined(Q_OS_LINUX) searchPaths << appDir.absoluteFilePath(lit("../../plugins-linux64/spirv/")); #endif searchPaths << appDir.absoluteFilePath(lit("../../plugins/")); path = QStandardPaths::findExecutable(exe, searchPaths); if(!path.isEmpty()) { SPIRVDisassembler dis; dis.name = ToQStr(tool); dis.executable = path; dis.tool = tool; SPIRVDisassemblers.push_back(dis); continue; } } return ret; }
QStringList PythonContext::completionOptions(QString base) { QStringList ret; if(!m_Completer) return ret; QByteArray bytes = base.toUtf8(); const char *input = (const char *)bytes.data(); PyGILState_STATE gil = PyGILState_Ensure(); PyObject *completeFunction = PyObject_GetAttrString(m_Completer, "complete"); int idx = 0; PyObject *opt = NULL; do { opt = PyObject_CallFunction(completeFunction, "si", input, idx); if(opt && opt != Py_None) { QString optstr = ToQStr(opt); bool add = true; // little hack, remove some of the ugly swig template instantiations that we can't avoid. if(optstr.contains(lit("renderdoc.rdcarray")) || optstr.contains(lit("renderdoc.rdcstr")) || optstr.contains(lit("renderdoc.bytebuf"))) add = false; if(add) ret << optstr; } idx++; } while(opt && opt != Py_None); // extra hack, remove the swig object functions/data but ONLY if we find a sure-fire identifier // (thisown) since otherwise we could remove append from a list object bool containsSwigInternals = false; for(const QString &optstr : ret) { if(optstr.contains(lit(".thisown"))) { containsSwigInternals = true; break; } } if(containsSwigInternals) { for(int i = 0; i < ret.count();) { if(ret[i].endsWith(lit(".acquire(")) || ret[i].endsWith(lit(".append(")) || ret[i].endsWith(lit(".disown(")) || ret[i].endsWith(lit(".next(")) || ret[i].endsWith(lit(".own(")) || ret[i].endsWith(lit(".this")) || ret[i].endsWith(lit(".thisown"))) ret.removeAt(i); else i++; } } Py_DecRef(completeFunction); PyGILState_Release(gil); return ret; }
void FetchException(QString &typeStr, QString &valueStr, int &finalLine, QList<QString> &frames) { PyObject *exObj = NULL, *valueObj = NULL, *tracebackObj = NULL; PyErr_Fetch(&exObj, &valueObj, &tracebackObj); PyErr_NormalizeException(&exObj, &valueObj, &tracebackObj); if(exObj && PyType_Check(exObj)) { PyTypeObject *type = (PyTypeObject *)exObj; typeStr = QString::fromUtf8(type->tp_name); } else { typeStr = QString(); } if(valueObj) valueStr = ToQStr(valueObj); if(tracebackObj) { PyObject *tracebackModule = PyImport_ImportModule("traceback"); if(tracebackModule) { PyObject *func = PyObject_GetAttrString(tracebackModule, "format_tb"); if(func && PyCallable_Check(func)) { PyObject *args = Py_BuildValue("(N)", tracebackObj); PyObject *formattedTB = PyObject_CallObject(func, args); PyTracebackObject *tb = (PyTracebackObject *)tracebackObj; while(tb->tb_next) tb = tb->tb_next; finalLine = tb->tb_lineno; if(formattedTB) { Py_ssize_t size = PyList_Size(formattedTB); for(Py_ssize_t i = 0; i < size; i++) { PyObject *el = PyList_GetItem(formattedTB, i); frames << ToQStr(el).trimmed(); } Py_DecRef(formattedTB); } Py_DecRef(args); } } } Py_DecRef(exObj); Py_DecRef(valueObj); Py_DecRef(tracebackObj); }