Improve workflow for capture import/export

* If the export doesn't need buffers, we export directly from the loaded
  capture file instead of re-loading it.
* Add progress bars for the load step so it shows what's happening
  instead of looking stalled.
* Reduce compression rate on XML+ZIP buffers as it took too long trying
  to compress when exporting large captures.
This commit is contained in:
baldurk
2018-02-18 18:01:54 +00:00
parent 48c2678fed
commit 934800793a
18 changed files with 425 additions and 244 deletions
+141 -4
View File
@@ -135,6 +135,8 @@ rdcstr CaptureContext::TempCaptureFilename(const rdcstr &appname)
void CaptureContext::LoadCapture(const rdcstr &captureFile, const rdcstr &origFilename,
bool temporary, bool local)
{
CloseCapture();
m_LoadInProgress = true;
if(local)
@@ -662,7 +664,7 @@ void CaptureContext::RecompressCapture()
{
// for remote files we open a new short-lived handle on the temporary file
tempCap = cap = RENDERDOC_OpenCaptureFile();
cap->OpenFile(tempFilename.toUtf8().data(), "rdc");
cap->OpenFile(tempFilename.toUtf8().data(), "rdc", NULL);
}
if(!cap)
@@ -691,7 +693,7 @@ void CaptureContext::RecompressCapture()
float progress = 0.0f;
LambdaThread *th = new LambdaThread([this, cap, destFilename, &progress]() {
cap->Convert(destFilename.toUtf8().data(), "rdc", [&progress](float p) { progress = p; });
cap->Convert(destFilename.toUtf8().data(), "rdc", NULL, [&progress](float p) { progress = p; });
});
th->start();
// wait a few ms before popping up a progress bar
@@ -709,7 +711,7 @@ void CaptureContext::RecompressCapture()
// the original, then re-open.
// this releases the hold over the real desired location.
cap->OpenFile("", "");
cap->OpenFile("", "", NULL);
// now remove the old capture
QFile::remove(GetCaptureFilename());
@@ -718,7 +720,7 @@ void CaptureContext::RecompressCapture()
QFile::rename(destFilename, GetCaptureFilename());
// and re-open
cap->OpenFile(GetCaptureFilename().c_str(), "rdc");
cap->OpenFile(GetCaptureFilename().c_str(), "rdc", NULL);
}
else
{
@@ -869,6 +871,141 @@ void CaptureContext::CloseCapture()
}
}
bool CaptureContext::ImportCapture(const CaptureFileFormat &fmt, const rdcstr &importfile,
const rdcstr &rdcfile)
{
CloseCapture();
QString ext = fmt.extension;
ReplayStatus status = ReplayStatus::UnknownError;
QString message;
// shorten the filename after here for error messages
QString filename = QFileInfo(importfile).fileName();
float progress = 0.0f;
LambdaThread *th = new LambdaThread([rdcfile, importfile, ext, &message, &progress, &status]() {
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
status = file->OpenFile(importfile.c_str(), ext.toUtf8().data(),
[&progress](float p) { progress = p * 0.5f; });
if(status != ReplayStatus::Succeeded)
{
message = file->ErrorString();
file->Shutdown();
return;
}
status = file->Convert(rdcfile.c_str(), "rdc", NULL,
[&progress](float p) { progress = 0.5f + p * 0.5f; });
file->Shutdown();
});
th->start();
// wait a few ms before popping up a progress bar
th->wait(500);
if(th->isRunning())
{
ShowProgressDialog(m_MainWindow, tr("Importing from %1, please wait...").arg(filename),
[th]() { return !th->isRunning(); }, [&progress]() { return progress; });
}
th->deleteLater();
if(status != ReplayStatus::Succeeded)
{
QString text = tr("Couldn't convert file '%1'\n").arg(filename);
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(m_MainWindow, tr("Error converting capture"), text);
return false;
}
return true;
}
void CaptureContext::ExportCapture(const CaptureFileFormat &fmt, const rdcstr &exportfile)
{
if(!m_CaptureLocal)
return;
QString ext = fmt.extension;
ICaptureFile *local = NULL;
ICaptureFile *file = NULL;
ReplayStatus status = ReplayStatus::Succeeded;
const SDFile *sdfile = NULL;
// if we don't need buffers, we can export directly from our existing capture file
if(!fmt.requiresBuffers)
{
file = Replay().GetCaptureFile();
sdfile = m_StructuredFile;
}
if(!file)
{
local = file = RENDERDOC_OpenCaptureFile();
status = file->OpenFile(m_CaptureFile.toUtf8().data(), "rdc", NULL);
}
QString filename = QFileInfo(m_CaptureFile).fileName();
if(status != ReplayStatus::Succeeded)
{
QString text = tr("Couldn't open file '%1' for export\n").arg(filename);
QString message = local->ErrorString();
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(m_MainWindow, tr("Error opening file"), text);
if(local)
local->Shutdown();
return;
}
float progress = 0.0f;
LambdaThread *th = new LambdaThread([this, file, sdfile, ext, exportfile, &progress, &status]() {
status = file->Convert(exportfile.c_str(), ext.toUtf8().data(), sdfile,
[&progress](float p) { progress = p; });
});
th->start();
// wait a few ms before popping up a progress bar
th->wait(500);
if(th->isRunning())
{
ShowProgressDialog(m_MainWindow,
tr("Exporting %1 to %2, please wait...").arg(filename).arg(QString(fmt.name)),
[th]() { return !th->isRunning(); }, [&progress]() { return progress; });
}
th->deleteLater();
QString message = file->ErrorString();
if(local)
local->Shutdown();
if(status != ReplayStatus::Succeeded)
{
QString text = tr("Couldn't convert file '%1'\n").arg(filename);
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(m_MainWindow, tr("Error converting capture"), text);
}
}
void CaptureContext::SetEventID(const rdcarray<ICaptureViewer *> &exclude, uint32_t selectedEventID,
uint32_t eventId, bool force)
{
+3 -1
View File
@@ -73,7 +73,9 @@ public:
bool SaveCaptureTo(const rdcstr &captureFile) override;
void RecompressCapture() override;
void CloseCapture() override;
bool ImportCapture(const CaptureFileFormat &fmt, const rdcstr &importfile,
const rdcstr &rdcfile) override;
void ExportCapture(const CaptureFileFormat &fmt, const rdcstr &exportfile) override;
void SetEventID(const rdcarray<ICaptureViewer *> &exclude, uint32_t selectedEventID,
uint32_t eventId, bool force = false) override;
+24
View File
@@ -1006,6 +1006,30 @@ time.
DOCUMENT("Close the currently open capture file.");
virtual void CloseCapture() = 0;
DOCUMENT(R"(Imports a capture file from a non-native format, via conversion to temporary rdc.
This converts the file to a specified temporary .rdc and loads it, closing any existing capture.
The capture must be available locally, if it's not this function will fail.
:param CaptureFileFormat fmt: The capture file format to import from.
:param str importfile: The path to import from.
:param str rdcfile: The temporary path to save the rdc file to.
:return: ``True`` if the import operation was successful and the capture was loaded.
:rtype: ``bool``
)");
virtual bool ImportCapture(const CaptureFileFormat &fmt, const rdcstr &importfile,
const rdcstr &rdcfile) = 0;
DOCUMENT(R"(Exports the current capture file to a given path with a specified capture file format.
The capture must be available locally, if it's not this function will fail.
:param CaptureFileFormat fmt: The capture file format to export to.
:param str exportfile: The path to export the capture file to.
)");
virtual void ExportCapture(const CaptureFileFormat &fmt, const rdcstr &exportfile) = 0;
DOCUMENT(R"(Move the current replay to a new event in the capture.
:param list exclude: A list of :class:`CaptureViewer` to exclude from being notified of this, to stop
+2 -2
View File
@@ -369,7 +369,7 @@ void ReplayManager::ReopenCaptureFile(const QString &path)
{
if(!m_CaptureFile)
m_CaptureFile = RENDERDOC_OpenCaptureFile();
m_CaptureFile->OpenFile(path.toUtf8().data(), "rdc");
m_CaptureFile->OpenFile(path.toUtf8().data(), "rdc", NULL);
}
uint32_t ReplayManager::ExecuteAndInject(const rdcstr &exe, const rdcstr &workingDir,
@@ -423,7 +423,7 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile,
{
m_CaptureFile = RENDERDOC_OpenCaptureFile();
m_CreateStatus = m_CaptureFile->OpenFile(capturefile.toUtf8().data(), "rdc");
m_CreateStatus = m_CaptureFile->OpenFile(capturefile.toUtf8().data(), "rdc", NULL);
if(m_CreateStatus == ReplayStatus::Succeeded)
std::tie(m_CreateStatus, m_Renderer) = m_CaptureFile->OpenCapture(progress);
+1 -1
View File
@@ -76,7 +76,7 @@ CrashDialog::CrashDialog(PersistantConfig &cfg, QVariantMap crashReportJSON, QWi
ICaptureFile *cap = RENDERDOC_OpenCaptureFile();
ReplayStatus status = cap->OpenFile(capInfo.absoluteFilePath().toUtf8().data(), "");
ReplayStatus status = cap->OpenFile(capInfo.absoluteFilePath().toUtf8().data(), "", NULL);
if(status == ReplayStatus::Succeeded)
{
+25 -131
View File
@@ -338,39 +338,29 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai
for(const CaptureFileFormat &fmt : formats)
{
QString name = fmt.name;
if(name == lit("rdc"))
if(fmt.extension == "rdc")
continue;
QString desc = fmt.description;
int idx = desc.indexOf(QLatin1Char('\n'));
QString title = idx >= 0 ? desc.mid(0, idx).trimmed() : desc.trimmed();
QString tooltip = idx >= 0 ? desc.mid(idx).trimmed() : QString();
if(fmt.openSupported)
{
QAction *action = new QAction(title, this);
QAction *action = new QAction(fmt.name, this);
QObject::connect(action, &QAction::triggered,
[this, name, title]() { importCapture(name, title); });
QObject::connect(action, &QAction::triggered, [this, fmt]() { importCapture(fmt); });
if(!tooltip.isEmpty())
action->setToolTip(tooltip);
if(!fmt.description.isEmpty())
action->setToolTip(fmt.description);
ui->menu_Import_From->addAction(action);
}
if(fmt.convertSupported)
{
QAction *action = new QAction(title, this);
QAction *action = new QAction(fmt.name, this);
QObject::connect(action, &QAction::triggered,
[this, name, title]() { exportCapture(name, title); });
QObject::connect(action, &QAction::triggered, [this, fmt]() { exportCapture(fmt); });
if(!tooltip.isEmpty())
action->setToolTip(tooltip);
if(!fmt.description.isEmpty())
action->setToolTip(fmt.description);
ui->menu_Export_As->addAction(action);
}
@@ -444,75 +434,30 @@ void MainWindow::on_action_Open_Capture_triggered()
LoadFromFilename(filename, false);
}
void MainWindow::importCapture(QString ext, QString title)
void MainWindow::importCapture(const CaptureFileFormat &fmt)
{
if(!PromptCloseCapture())
return;
QString ext = fmt.extension;
QString title = fmt.name;
QString filename =
RDDialog::getOpenFileName(this, tr("Select file to open"), m_Ctx.Config().LastCaptureFilePath,
tr("%1 Files (*.%2);;All Files (*)").arg(title).arg(ext));
if(!filename.isEmpty())
{
QString rdcfile = m_Ctx.TempCaptureFilename("imported");
QString rdcfile = m_Ctx.TempCaptureFilename(lit("imported_") + ext);
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
bool success = m_Ctx.ImportCapture(fmt, filename, rdcfile);
ReplayStatus status = file->OpenFile(filename.toUtf8().data(), ext.toUtf8().data());
filename = QFileInfo(filename).fileName();
if(status != ReplayStatus::Succeeded)
if(success)
{
QString text = tr("Couldn't open file '%1'\n").arg(filename);
QString message = file->ErrorString();
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(this, tr("Error opening file"), text);
file->Shutdown();
return;
// open file as temporary, in case the user wants to save the imported rdc
LoadFromFilename(rdcfile, true);
takeCaptureOwnership();
}
float progress = 0.0f;
LambdaThread *th = new LambdaThread([file, rdcfile, &progress, &status]() {
status = file->Convert(rdcfile.toUtf8().data(), "rdc", [&progress](float p) { progress = p; });
});
th->start();
// wait a few ms before popping up a progress bar
th->wait(500);
if(th->isRunning())
{
ShowProgressDialog(this, tr("Importing from %1, please wait...").arg(filename),
[th]() { return !th->isRunning(); }, [&progress]() { return progress; });
}
th->deleteLater();
if(status != ReplayStatus::Succeeded)
{
QString text = tr("Couldn't convert file '%1'\n").arg(filename);
QString message = file->ErrorString();
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(this, tr("Error converting capture"), text);
file->Shutdown();
return;
}
file->Shutdown();
// open file as temporary, in case the user wants to save the imported rdc
LoadFromFilename(rdcfile, true);
takeCaptureOwnership();
}
}
@@ -647,7 +592,7 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
ReplayStatus status = file->OpenFile(filename.toUtf8().data(), "rdc");
ReplayStatus status = file->OpenFile(filename.toUtf8().data(), "rdc", NULL);
if(status != ReplayStatus::Succeeded)
{
@@ -880,9 +825,9 @@ bool MainWindow::PromptSaveCaptureAs()
return false;
}
void MainWindow::exportCapture(QString ext, QString title)
void MainWindow::exportCapture(const CaptureFileFormat &fmt)
{
if(m_Ctx.Replay().CurrentRemote())
if(!m_Ctx.IsCaptureLocal())
{
RDDialog::information(
this, tr("Save changes to capture?"),
@@ -892,62 +837,11 @@ void MainWindow::exportCapture(QString ext, QString title)
}
QString saveFilename =
GetSavePath(tr("Export Capture As"), tr("%1 Files (*.%2)").arg(title).arg(ext));
GetSavePath(tr("Export Capture As"),
tr("%1 Files (*.%2)").arg(QString(fmt.name)).arg(QString(fmt.extension)));
if(!saveFilename.isEmpty())
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
ReplayStatus status = file->OpenFile(m_Ctx.GetCaptureFilename().c_str(), "rdc");
QString filename = QFileInfo(QString(m_Ctx.GetCaptureFilename())).fileName();
if(status != ReplayStatus::Succeeded)
{
QString text = tr("Couldn't open file '%1' for export\n").arg(filename);
QString message = file->ErrorString();
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(this, tr("Error opening file"), text);
file->Shutdown();
return;
}
float progress = 0.0f;
LambdaThread *th = new LambdaThread([file, ext, saveFilename, &progress, &status]() {
status = file->Convert(saveFilename.toUtf8().data(), ext.toUtf8().data(),
[&progress](float p) { progress = p; });
});
th->start();
// wait a few ms before popping up a progress bar
th->wait(500);
if(th->isRunning())
{
ShowProgressDialog(this, tr("Exporting %1 to %2, please wait...").arg(filename).arg(title),
[th]() { return !th->isRunning(); }, [&progress]() { return progress; });
}
th->deleteLater();
QString message = file->ErrorString();
file->Shutdown();
if(status != ReplayStatus::Succeeded)
{
QString text = tr("Couldn't convert file '%1'\n").arg(filename);
if(message.isEmpty())
text += tr("%1").arg(ToQStr(status));
else
text += tr("%1: %2").arg(ToQStr(status)).arg(message);
RDDialog::critical(this, tr("Error converting capture"), text);
return;
}
}
m_Ctx.ExportCapture(fmt, saveFilename);
}
bool MainWindow::PromptCloseCapture()
+2 -2
View File
@@ -162,8 +162,8 @@ private:
bool eventFilter(QObject *watched, QEvent *event) override;
void importCapture(QString ext, QString title);
void exportCapture(QString ext, QString title);
void importCapture(const CaptureFileFormat &fmt);
void exportCapture(const CaptureFileFormat &fmt);
QString dragFilename(const QMimeData *mimeData);
+9
View File
@@ -183,6 +183,15 @@ struct CaptureContextInvoker : ICaptureContext
InvokeVoidFunction(&ICaptureContext::RecompressCapture);
}
virtual void CloseCapture() override { InvokeVoidFunction(&ICaptureContext::CloseCapture); }
virtual bool ImportCapture(const CaptureFileFormat &fmt, const rdcstr &importfile,
const rdcstr &rdcfile) override
{
return InvokeRetFunction<bool>(&ICaptureContext::ImportCapture, fmt, importfile, rdcfile);
}
virtual void ExportCapture(const CaptureFileFormat &fmt, const rdcstr &exportfile) override
{
InvokeVoidFunction(&ICaptureContext::ExportCapture, fmt, exportfile);
}
virtual void SetEventID(const rdcarray<ICaptureViewer *> &exclude, uint32_t selectedEventID,
uint32_t eventId, bool force = false) override
{
+19 -5
View File
@@ -536,34 +536,48 @@ struct CaptureFileFormat
DOCUMENT("");
bool operator==(const CaptureFileFormat &o) const
{
return name == o.name && description == o.description && openSupported == o.openSupported &&
return extension == o.extension && name == o.name && description == o.description &&
requiresBuffers == o.requiresBuffers && openSupported == o.openSupported &&
convertSupported == o.convertSupported;
}
bool operator<(const CaptureFileFormat &o) const
{
if(!(extension == o.extension))
return extension < o.extension;
if(!(name == o.name))
return name < o.name;
if(!(description == o.description))
return description < o.description;
if(!(requiresBuffers == o.requiresBuffers))
return requiresBuffers < o.requiresBuffers;
if(!(openSupported == o.openSupported))
return openSupported < o.openSupported;
if(!(convertSupported == o.convertSupported))
return convertSupported < o.convertSupported;
return false;
}
DOCUMENT("The name of the format as a single minimal string, e.g. ``rdc``.");
DOCUMENT("The file of the format as a single minimal string, e.g. ``rdc``.");
rdcstr extension;
DOCUMENT("A human readable short phrase naming the file format.");
rdcstr name;
DOCUMENT("A human readable description of the file format, e.g. ``RenderDoc native capture``.");
DOCUMENT("A human readable long-form description of the file format.");
rdcstr description;
DOCUMENT(R"(Indicates whether exporting to this format requires buffers or just structured data.
If it doesn't require buffers then it can be exported directly from an opened capture, which by
default has structured data but no buffers available.
)");
bool requiresBuffers;
DOCUMENT(R"(Indicates whether or not files in this format can be opened and processed as
structured data.
)");
bool openSupported = false;
bool openSupported;
DOCUMENT("Indicates whether captures or structured data can be saved out in this format.");
bool convertSupported = true;
bool convertSupported;
};
DECLARE_REFLECTION_STRUCT(CaptureFileFormat);
+13 -3
View File
@@ -1486,10 +1486,13 @@ empty or unrecognised.
:param str filename: The filename of the file to open.
:param str filetype: The format of the given file.
:param ProgressCallback progress: A callback that will be repeatedly called with an updated progress
value if an import step occurs. Can be ``None`` if no progress is desired.
:return: The status of the open operation, whether it succeeded or failed (and how it failed).
:rtype: ReplayStatus
)");
virtual ReplayStatus OpenFile(const char *filename, const char *filetype) = 0;
virtual ReplayStatus OpenFile(const char *filename, const char *filetype,
RENDERDOC_ProgressCallback progress) = 0;
DOCUMENT(R"(Initialises the file handle from a raw memory buffer.
@@ -1499,10 +1502,13 @@ For the :paramref:`OpenBuffer.filetype` parameter, see :meth:`OpenFile`.
:param bytes buffer: The buffer containing the data to process.
:param str filetype: The format of the given file.
:param ProgressCallback progress: A callback that will be repeatedly called with an updated progress
value if an import step occurs. Can be ``None`` if no progress is desired.
:return: The status of the open operation, whether it succeeded or failed (and how it failed).
:rtype: ReplayStatus
)");
virtual ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype) = 0;
virtual ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype,
RENDERDOC_ProgressCallback progress) = 0;
DOCUMENT(R"(When a capture file is opened, an exclusive lock is held on the file on disk. This
makes it impossible to copy the file to another location at the user's request. Calling this
@@ -1525,12 +1531,16 @@ representation back to native RDC.
:param str filename: The filename to save to.
:param str filetype: The format to convert to.
:param SDFile file: An optional :class:`SDFile` with the structured data to source from. This is
useful in case the format specifies that it doesn't need buffers, and you already have a
:class:`ReplayController` open with the structured data. This saves the need to load the file
again. If ``None`` then structured data will be fetched if not already present and used.
:param ProgressCallback progress: A callback that will be repeatedly called with an updated progress
value for the conversion. Can be ``None`` if no progress is desired.
:return: The status of the conversion operation, whether it succeeded or failed (and how it failed).
:rtype: ReplayStatus
)");
virtual ReplayStatus Convert(const char *filename, const char *filetype,
virtual ReplayStatus Convert(const char *filename, const char *filetype, const SDFile *file,
RENDERDOC_ProgressCallback progress) = 0;
DOCUMENT(R"(Returns the human-readable error string for the last error received.
+38 -26
View File
@@ -788,22 +788,45 @@ void RenderDoc::RegisterStructuredProcessor(RDCDriver driver, StructuredProcesso
m_StructProcesssors[driver] = provider;
}
void RenderDoc::RegisterCaptureExporter(const char *filetype, const char *description,
CaptureExporter exporter)
void RenderDoc::RegisterCaptureExporter(CaptureExporter exporter, CaptureFileFormat description)
{
RDCASSERT(m_ImportExportFormats.find(filetype) == m_ImportExportFormats.end());
std::string filetype = description.extension;
m_ImportExportFormats[filetype] = description;
for(const CaptureFileFormat &fmt : m_ImportExportFormats)
{
if(fmt.extension == filetype)
{
RDCERR("Duplicate exporter for '%s' found", filetype.c_str());
return;
}
}
description.openSupported = false;
description.convertSupported = true;
m_ImportExportFormats.push_back(description);
m_Exporters[filetype] = exporter;
}
void RenderDoc::RegisterCaptureImportExporter(const char *filetype, const char *description,
CaptureImporter importer, CaptureExporter exporter)
void RenderDoc::RegisterCaptureImportExporter(CaptureImporter importer, CaptureExporter exporter,
CaptureFileFormat description)
{
RDCASSERT(m_ImportExportFormats.find(filetype) == m_ImportExportFormats.end());
std::string filetype = description.extension;
m_ImportExportFormats[filetype] = description;
for(const CaptureFileFormat &fmt : m_ImportExportFormats)
{
if(fmt.extension == filetype)
{
RDCERR("Duplicate import/exporter for '%s' found", filetype.c_str());
return;
}
}
description.openSupported = true;
description.convertSupported = true;
m_ImportExportFormats.push_back(description);
m_Importers[filetype] = importer;
m_Exporters[filetype] = exporter;
@@ -847,30 +870,19 @@ CaptureImporter RenderDoc::GetCaptureImporter(const char *filetype)
std::vector<CaptureFileFormat> RenderDoc::GetCaptureFileFormats()
{
std::vector<CaptureFileFormat> ret;
std::vector<CaptureFileFormat> ret = m_ImportExportFormats;
std::sort(ret.begin(), ret.end());
{
CaptureFileFormat rdc;
rdc.name = "rdc";
rdc.description = "Native RDC capture file format.";
rdc.extension = "rdc";
rdc.name = "Native RDC capture file format.";
rdc.description = "The format produced by frame-captures from applications directly.";
rdc.openSupported = true;
rdc.convertSupported = true;
ret.push_back(rdc);
}
for(auto it = m_ImportExportFormats.begin(); it != m_ImportExportFormats.end(); ++it)
{
CaptureFileFormat fmt;
fmt.name = it->first;
fmt.description = it->second;
fmt.openSupported = m_Importers.find(it->first) != m_Importers.end();
fmt.convertSupported = m_Exporters.find(it->first) != m_Exporters.end();
RDCASSERT(fmt.openSupported || fmt.convertSupported);
ret.push_back(fmt);
ret.insert(ret.begin(), rdc);
}
return ret;
+9 -12
View File
@@ -312,8 +312,6 @@ class IReplayDriver;
class StreamReader;
class RDCFile;
class RDCFile;
typedef ReplayStatus (*RemoteDriverProvider)(RDCFile *rdc, IRemoteDriver **driver);
typedef ReplayStatus (*ReplayDriverProvider)(RDCFile *rdc, IReplayDriver **driver);
@@ -430,10 +428,9 @@ public:
void RegisterStructuredProcessor(RDCDriver driver, StructuredProcessor provider);
void RegisterCaptureExporter(const char *filetype, const char *description,
CaptureExporter exporter);
void RegisterCaptureImportExporter(const char *filetype, const char *description,
CaptureImporter importer, CaptureExporter exporter);
void RegisterCaptureExporter(CaptureExporter exporter, CaptureFileFormat description);
void RegisterCaptureImportExporter(CaptureImporter importer, CaptureExporter exporter,
CaptureFileFormat description);
StructuredProcessor GetStructuredProcessor(RDCDriver driver);
@@ -584,7 +581,7 @@ private:
std::map<RDCDriver, StructuredProcessor> m_StructProcesssors;
std::map<std::string, std::string> m_ImportExportFormats;
std::vector<CaptureFileFormat> m_ImportExportFormats;
std::map<std::string, CaptureImporter> m_Importers;
std::map<std::string, CaptureExporter> m_Exporters;
@@ -674,13 +671,13 @@ struct StructuredProcessRegistration
struct ConversionRegistration
{
ConversionRegistration(const char *filetype, const char *description, CaptureImporter importer,
CaptureExporter exporter)
ConversionRegistration(CaptureImporter importer, CaptureExporter exporter,
CaptureFileFormat description)
{
RenderDoc::Inst().RegisterCaptureImportExporter(filetype, description, importer, exporter);
RenderDoc::Inst().RegisterCaptureImportExporter(importer, exporter, description);
}
ConversionRegistration(const char *filetype, const char *description, CaptureExporter exporter)
ConversionRegistration(CaptureExporter exporter, CaptureFileFormat description)
{
RenderDoc::Inst().RegisterCaptureExporter(filetype, description, exporter);
RenderDoc::Inst().RegisterCaptureExporter(exporter, description);
}
};
+1 -1
View File
@@ -179,7 +179,7 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli
bytebuf buf;
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
if(file->OpenFile(captures.back().path.c_str(), "rdc") == ReplayStatus::Succeeded)
if(file->OpenFile(captures.back().path.c_str(), "rdc", NULL) == ReplayStatus::Succeeded)
{
buf = file->GetThumbnail(FileType::JPG, 0).data;
}
+1 -1
View File
@@ -1552,7 +1552,7 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct
// only set progress after we've initialised the debug manager, to prevent progress jumping
// backwards.
if(m_DebugManager)
if(m_DebugManager || IsStructuredExporting(m_State))
{
RenderDoc::Inst().SetProgress(LoadProgress::FileInitialRead,
float(offsetEnd) / float(reader->GetSize()));
+74 -21
View File
@@ -112,8 +112,10 @@ public:
CaptureFile();
virtual ~CaptureFile();
ReplayStatus OpenFile(const char *filename, const char *filetype);
ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype);
ReplayStatus OpenFile(const char *filename, const char *filetype,
RENDERDOC_ProgressCallback progress);
ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype,
RENDERDOC_ProgressCallback progress);
bool CopyFileTo(const char *filename);
rdcstr ErrorString() { return m_ErrorString; }
void Shutdown() { delete this; }
@@ -125,7 +127,7 @@ public:
void SetMetadata(const char *driverName, uint64_t machineIdent, FileType thumbType,
uint32_t thumbWidth, uint32_t thumbHeight, const bytebuf &thumbData);
ReplayStatus Convert(const char *filename, const char *filetype,
ReplayStatus Convert(const char *filename, const char *filetype, const SDFile *file,
RENDERDOC_ProgressCallback progress);
rdcarray<CaptureFileFormat> GetCaptureFileFormats()
@@ -135,16 +137,8 @@ public:
const SDFile &GetStructuredData()
{
if(m_StructuredData.chunks.empty() && m_RDC && m_RDC->SectionIndex(SectionType::FrameCapture) >= 0)
{
// decompile to structured data on demand.
StructuredProcessor proc = RenderDoc::Inst().GetStructuredProcessor(m_RDC->GetDriver());
if(proc)
proc(m_RDC, m_StructuredData);
else
RDCERR("Can't get structured data for driver %s", m_RDC->GetDriverName().c_str());
}
// decompile to structured data on demand.
InitStructuredData();
return m_StructuredData;
}
@@ -182,6 +176,8 @@ public:
private:
ReplayStatus Init();
void InitStructuredData(RENDERDOC_ProgressCallback progress = RENDERDOC_ProgressCallback());
RDCFile *m_RDC = NULL;
Callstack::StackResolver *m_Resolver = NULL;
@@ -201,7 +197,8 @@ CaptureFile::~CaptureFile()
SAFE_DELETE(m_Resolver);
}
ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype)
ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype,
RENDERDOC_ProgressCallback progress)
{
CaptureImporter importer = RenderDoc::Inst().GetCaptureImporter(filetype);
@@ -213,7 +210,7 @@ ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype)
StreamReader reader(FileIO::fopen(filename, "rb"));
delete m_RDC;
m_RDC = new RDCFile;
ret = importer(filename, reader, m_RDC, m_StructuredData, NULL);
ret = importer(filename, reader, m_RDC, m_StructuredData, progress);
}
if(ret != ReplayStatus::Succeeded)
@@ -228,15 +225,22 @@ ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype)
if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc"))
RDCWARN("Opening file with unrecognised filetype '%s' - treating as 'rdc'", filetype);
if(progress)
progress(0.0f);
delete m_RDC;
m_RDC = new RDCFile;
m_RDC->Open(filename);
if(progress)
progress(1.0f);
}
return Init();
}
ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype)
ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype,
RENDERDOC_ProgressCallback progress)
{
CaptureImporter importer = RenderDoc::Inst().GetCaptureImporter(filetype);
@@ -249,7 +253,7 @@ ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype
{
StreamReader reader(vec);
m_RDC = new RDCFile;
ret = importer(NULL, reader, m_RDC, m_StructuredData, NULL);
ret = importer(NULL, reader, m_RDC, m_StructuredData, progress);
}
if(ret != ReplayStatus::Succeeded)
@@ -264,8 +268,14 @@ ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype
if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc"))
RDCWARN("Opening file with unrecognised filetype '%s' - treating as 'rdc'", filetype);
if(progress)
progress(0.0f);
m_RDC = new RDCFile;
m_RDC->Open(vec);
if(progress)
progress(1.0f);
}
return Init();
@@ -325,6 +335,23 @@ ReplayStatus CaptureFile::Init()
return ReplayStatus::InternalError;
}
void CaptureFile::InitStructuredData(RENDERDOC_ProgressCallback progress /*= RENDERDOC_ProgressCallback()*/)
{
if(m_StructuredData.chunks.empty() && m_RDC && m_RDC->SectionIndex(SectionType::FrameCapture) >= 0)
{
StructuredProcessor proc = RenderDoc::Inst().GetStructuredProcessor(m_RDC->GetDriver());
RenderDoc::Inst().SetProgressCallback<LoadProgress>(progress);
if(proc)
proc(m_RDC, m_StructuredData);
else
RDCERR("Can't get structured data for driver %s", m_RDC->GetDriverName().c_str());
RenderDoc::Inst().SetProgressCallback<LoadProgress>(RENDERDOC_ProgressCallback());
}
}
rdcpair<ReplayStatus, IReplayController *> CaptureFile::OpenCapture(RENDERDOC_ProgressCallback progress)
{
if(!m_RDC || m_RDC->ErrorCode() != ContainerError::NoError)
@@ -377,7 +404,7 @@ void CaptureFile::SetMetadata(const char *driverName, uint64_t machineIdent, Fil
free((void *)th.pixels);
}
ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype,
ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype, const SDFile *file,
RENDERDOC_ProgressCallback progress)
{
if(!m_RDC)
@@ -386,10 +413,30 @@ ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype,
return ReplayStatus::FileCorrupted;
}
// make sure progress is valid so we don't have to check it everywhere
if(!progress)
progress = [](float) {};
// we have two separate steps that can take time - fetching the structured data, and then
// exporting or writing to RDC
RENDERDOC_ProgressCallback fetchProgress = [progress](float p) { progress(p * 0.5f); };
RENDERDOC_ProgressCallback exportProgress = [progress](float p) { progress(0.5f + p * 0.5f); };
CaptureExporter exporter = RenderDoc::Inst().GetCaptureExporter(filetype);
if(exporter)
return exporter(filename, *m_RDC, GetStructuredData(), progress);
{
if(file)
{
return exporter(filename, *m_RDC, *file, exportProgress);
}
else
{
InitStructuredData(fetchProgress);
return exporter(filename, *m_RDC, GetStructuredData(), exportProgress);
}
}
if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc"))
RDCWARN("Converting file to unrecognised filetype '%s' - treating as 'rdc'", filetype);
@@ -419,17 +466,23 @@ ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype,
if(frameCaptureIndex == -1)
{
if(file == NULL)
{
InitStructuredData(fetchProgress);
file = &m_StructuredData;
}
SectionProperties frameCapture;
frameCapture.flags = SectionFlags::ZstdCompressed;
frameCapture.type = SectionType::FrameCapture;
frameCapture.name = ToStr(frameCapture.type);
frameCapture.version = GetStructuredData().version;
frameCapture.version = file->version;
StreamWriter *writer = output.WriteSection(frameCapture);
WriteSerialiser ser(writer, Ownership::Nothing);
ser.WriteStructuredFile(GetStructuredData(), progress);
ser.WriteStructuredFile(*file, exportProgress);
writer->Finish();
@@ -92,9 +92,11 @@ ReplayStatus exportChrome(const char *filename, const RDCFile &rdc, const SDFile
return ReplayStatus::Succeeded;
}
static ConversionRegistration XMLConversionRegistration("chrome.json", R"(Chrome profiler JSON
Exports the chunk threadID, timestamp and duration data to a JSON format that can be loaded by
chrome's profiler at chrome://tracing
)",
&exportChrome);
static ConversionRegistration XMLConversionRegistration(
&exportChrome,
{
"chrome.json", "Chrome profiler JSON",
R"(Exports the chunk threadID, timestamp and duration data to a JSON format that can be loaded
by chrome's profiler at chrome://tracing)",
false,
});
+43 -18
View File
@@ -289,6 +289,9 @@ static ReplayStatus Structured2XML(const char *filename, const RDCFile &file, ui
}
}
if(progress)
progress(StructuredProgress(0.1f));
// write all other sections
for(int i = 0; i < file.NumSections(); i++)
{
@@ -340,6 +343,9 @@ static ReplayStatus Structured2XML(const char *filename, const RDCFile &file, ui
delete reader;
}
if(progress)
progress(StructuredProgress(0.2f));
pugi::xml_node xChunks = xRoot.append_child("chunks");
xChunks.append_attribute("version") = version;
@@ -382,6 +388,9 @@ static ReplayStatus Structured2XML(const char *filename, const RDCFile &file, ui
for(size_t o = 0; o < chunk->data.children.size(); o++)
Obj2XML(xChunk, *chunk->data.children[o]);
}
if(progress)
progress(StructuredProgress(0.2f + 0.8f * (float(c) / float(chunks.size()))));
}
xml_file_writer writer(filename);
@@ -712,8 +721,7 @@ static ReplayStatus Buffers2ZIP(const std::string &filename, const RDCFile &file
for(size_t i = 0; i < buffers.size(); i++)
{
mz_zip_writer_add_mem(&zip, GetBufferName(i).c_str(), buffers[i]->data(), buffers[i]->size(),
MZ_BEST_COMPRESSION);
mz_zip_writer_add_mem(&zip, GetBufferName(i).c_str(), buffers[i]->data(), buffers[i]->size(), 2);
if(progress)
progress(BufferProgress(float(i) / float(buffers.size())));
@@ -729,13 +737,17 @@ static ReplayStatus Buffers2ZIP(const std::string &filename, const RDCFile &file
return ReplayStatus::Succeeded;
}
static void ZIP2Buffers(const std::string &filename, StructuredBufferList &buffers,
static bool ZIP2Buffers(const std::string &filename, StructuredBufferList &buffers,
RENDERDOC_ProgressCallback progress)
{
std::string zipFile = filename + ".zip";
std::string zipFile = filename;
zipFile.erase(zipFile.size() - 4); // remove the .xml, leave only the .zip
if(!FileIO::exists(zipFile.c_str()))
return;
{
RDCERR("Expected to file zip for %s at %s", filename.c_str(), zipFile.c_str());
return false;
}
mz_zip_archive zip;
memset(&zip, 0, sizeof(zip));
@@ -780,13 +792,22 @@ static void ZIP2Buffers(const std::string &filename, StructuredBufferList &buffe
}
mz_zip_reader_end(&zip);
return true;
}
ReplayStatus importXMLZ(const char *filename, StreamReader &reader, RDCFile *rdc,
SDFile &structData, RENDERDOC_ProgressCallback progress)
{
if(filename)
ZIP2Buffers(filename, structData.buffers, progress);
{
bool success = ZIP2Buffers(filename, structData.buffers, progress);
if(!success)
{
RDCERR("Couldn't load zip to go with %s", filename);
return ReplayStatus::FileCorrupted;
}
}
uint64_t len = reader.GetSize();
char *buf = new char[(size_t)len + 1];
@@ -814,16 +835,20 @@ ReplayStatus exportXMLOnly(const char *filename, const RDCFile &rdc, const SDFil
return Structured2XML(filename, rdc, structData.version, structData.chunks, progress);
}
static ConversionRegistration XMLZIPConversionRegistration("zip.xml", R"(XML+ZIP capture
static ConversionRegistration XMLZIPConversionRegistration(
&importXMLZ, &exportXMLZ,
{
"zip.xml", "XML+ZIP capture",
R"(Stores the structured data in an xml tree, with large buffer data stored in indexed blobs in
similarly named zip file.)",
true,
});
Stores the structured data in an xml tree, with large buffer data stored in indexed blobs in
similarly named zip file.
)",
&importXMLZ, &exportXMLZ);
static ConversionRegistration XMLOnlyConversionRegistration("xml", R"(XML capture
Stores the structured data in an xml tree, with large buffer data omitted - that makes it easier to
work with but it cannot then be imported.
)",
&exportXMLOnly);
static ConversionRegistration XMLOnlyConversionRegistration(
&exportXMLOnly,
{
"xml", "XML capture",
R"(Stores the structured data in an xml tree, with large buffer data omitted - that makes it
easier to work with but it cannot then be imported.)",
false,
});
+12 -10
View File
@@ -282,7 +282,7 @@ struct ThumbCommand : public Command
bytebuf buf;
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
ReplayStatus st = file->OpenFile(filename.c_str(), "rdc");
ReplayStatus st = file->OpenFile(filename.c_str(), "rdc", NULL);
if(st == ReplayStatus::Succeeded)
{
buf = file->GetThumbnail(type, maxsize).data;
@@ -591,7 +591,7 @@ struct ReplayCommand : public Command
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
if(file->OpenFile(filename.c_str(), "rdc") != ReplayStatus::Succeeded)
if(file->OpenFile(filename.c_str(), "rdc", NULL) != ReplayStatus::Succeeded)
{
std::cerr << "Couldn't load '" << filename << "'." << std::endl;
return 1;
@@ -657,7 +657,9 @@ struct ConvertCommand : public Command
{
std::cout << "Available formats:" << std::endl;
for(CaptureFileFormat f : m_Formats)
std::cout << "'" << (std::string)f.name << "': " << (std::string)f.description << std::endl;
std::cout << "'" << (std::string)f.name << "': " << (std::string)f.name << std::endl
<< std::endl
<< (std::string)f.description << std::endl;
return 0;
}
@@ -687,11 +689,11 @@ struct ConvertCommand : public Command
for(CaptureFileFormat f : m_Formats)
{
string extension = ".";
extension += f.name;
extension += f.extension;
if(infile.find(extension.c_str()) != string::npos)
{
infmt = f.name;
infmt = f.extension;
break;
}
}
@@ -710,11 +712,11 @@ struct ConvertCommand : public Command
for(CaptureFileFormat f : m_Formats)
{
string extension = ".";
extension += f.name;
extension += f.extension;
if(outfile.find(extension.c_str()) != string::npos)
{
outfmt = f.name;
outfmt = f.extension;
break;
}
}
@@ -729,7 +731,7 @@ struct ConvertCommand : public Command
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
ReplayStatus st = file->OpenFile(infile.c_str(), infmt.c_str());
ReplayStatus st = file->OpenFile(infile.c_str(), infmt.c_str(), NULL);
if(st != ReplayStatus::Succeeded)
{
@@ -738,7 +740,7 @@ struct ConvertCommand : public Command
return 1;
}
st = file->Convert(outfile.c_str(), outfmt.c_str(), NULL);
st = file->Convert(outfile.c_str(), outfmt.c_str(), NULL, NULL);
if(st != ReplayStatus::Succeeded)
{
@@ -956,7 +958,7 @@ struct EmbeddedSectionCommand : public Command
ICaptureFile *capfile = RENDERDOC_OpenCaptureFile();
ReplayStatus status = capfile->OpenFile(rdc.c_str(), "");
ReplayStatus status = capfile->OpenFile(rdc.c_str(), "", NULL);
if(status != ReplayStatus::Succeeded)
{