Change core capture code to be more centred around container handle

* When opening a capture file, a format is now available to allow
  easy import from another format without a completely different
  interface. Only rdc files can be replayed, but any other file can
  load and access structured data through the same interface.
* The replay initialisation and capture writing interfaces also use the
  RDCFile instead of passing filenames or Serialisers around directly.
  Driver initialisation parameters are now entirely private, and don't
  need to be exposed - any agnostic metadata like thumbnail, driver, etc
  are all accessed via the RDCFile container itself.
* Callstack resolution is now part of the container file, not the
  back-end via way of its Serialiser.
* Importers/Exporters to other non-RDC formats are registered in a
  similar way to replay/remote drivers.
* It is also then possible to construct an RDC file from thin air, by
  creating an empty RDCFile container and filling it with data, then
  requesting it to be written to disk.
This commit is contained in:
baldurk
2017-11-07 19:30:35 +00:00
parent a63e99177d
commit 9388d2b71b
26 changed files with 1036 additions and 593 deletions
+7
View File
@@ -634,6 +634,13 @@ struct IReplayManager
)");
virtual const RemoteHost *CurrentRemote() = 0;
DOCUMENT(R"(Retrieves the capture file handle for the currently open file.
:return: The file handle active, or ``None`` if no capture is open.
:rtype: StackResolver
)");
virtual IStackResolver *GetResolver() = 0;
DOCUMENT(R"(Launch an application and inject into it to allow capturing.
This happens either locally, or on the remote server, depending on whether a connection is active.
+9 -5
View File
@@ -407,14 +407,12 @@ void ReplayManager::run()
}
else
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile(m_Logfile.toUtf8().data());
m_CaptureFile = RENDERDOC_OpenCaptureFile();
m_CreateStatus = file->OpenStatus();
m_CreateStatus = m_CaptureFile->OpenFile(m_Logfile.toUtf8().data(), "rdc");
if(m_CreateStatus == ReplayStatus::Succeeded)
std::tie(m_CreateStatus, m_Renderer) = file->OpenCapture(m_Progress);
file->Shutdown();
std::tie(m_CreateStatus, m_Renderer) = m_CaptureFile->OpenCapture(m_Progress);
}
if(m_Renderer == NULL)
@@ -479,4 +477,10 @@ void ReplayManager::run()
m_Remote->CloseCapture(m_Renderer);
else
m_Renderer->Shutdown();
m_Renderer = NULL;
if(m_CaptureFile)
m_CaptureFile->Shutdown();
m_CaptureFile = NULL;
}
+9
View File
@@ -75,6 +75,14 @@ public:
void ShutdownServer();
void PingRemote();
IStackResolver *GetResolver()
{
if(m_CaptureFile)
return m_CaptureFile;
if(m_Remote)
return m_Remote;
return NULL;
}
const RemoteHost *CurrentRemote() { return m_RemoteHost; }
uint32_t ExecuteAndInject(const QString &exe, const QString &workingDir, const QString &cmdLine,
const QList<EnvironmentModification> &env, const QString &logfile,
@@ -108,6 +116,7 @@ private:
QQueue<InvokeHandle *> m_RenderQueue;
QWaitCondition m_RenderCondition;
ICaptureFile *m_CaptureFile = NULL;
IReplayController *m_Renderer = NULL;
void PushInvoke(InvokeHandle *cmd);
+14 -4
View File
@@ -101,11 +101,21 @@ void APIInspector::on_apiEvents_itemSelectionChanged()
if(!ev.callstack.isEmpty())
{
m_Ctx.Replay().AsyncInvoke([this, ev](IReplayController *r) {
rdcarray<rdcstr> trace = r->GetResolve(ev.callstack);
if(m_Ctx.Replay().GetResolver())
{
m_Ctx.Replay().AsyncInvoke([this, ev](IReplayController *) {
rdcarray<rdcstr> stack = m_Ctx.Replay().GetResolver()->GetResolve(ev.callstack);
GUIInvoke::call([this, trace]() { addCallstack(trace); });
});
GUIInvoke::call([this, stack]() { addCallstack(stack); });
});
}
else
{
ui->callstack->setUpdatesEnabled(false);
ui->callstack->clear();
ui->callstack->addItem(tr("Callstack resolution not available."));
ui->callstack->setUpdatesEnabled(true);
}
}
else
{
+33 -12
View File
@@ -402,9 +402,9 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local
if(local)
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename.toUtf8().data());
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
if(file->OpenStatus() != ReplayStatus::Succeeded)
if(file->OpenFile(filename.toUtf8().data(), "rdc") != ReplayStatus::Succeeded)
{
RDDialog::critical(NULL, tr("Error opening capture"),
tr("Couldn't open file '%1'").arg(filename));
@@ -1275,18 +1275,20 @@ void MainWindow::OnLogfileLoaded()
setLogHasErrors(!m_Ctx.DebugMessages().empty());
m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) {
bool hasResolver = r->HasCallstacks();
ui->action_Resolve_Symbols->setEnabled(false);
m_Ctx.Replay().AsyncInvoke([this](IReplayController *) {
bool hasResolver = m_Ctx.Replay().GetResolver()->HasCallstacks();
GUIInvoke::call([this, hasResolver]() {
ui->action_Resolve_Symbols->setEnabled(hasResolver);
ui->action_Resolve_Symbols->setText(hasResolver ? tr("Resolve Symbols")
: tr("Resolve Symbols - None in log"));
ui->action_Save_Log->setEnabled(true);
});
});
ui->action_Save_Log->setEnabled(true);
SetTitle();
PopulateRecentFiles();
@@ -1540,15 +1542,34 @@ void MainWindow::on_action_Python_Shell_triggered()
void MainWindow::on_action_Resolve_Symbols_triggered()
{
m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { r->InitResolver(); });
if(!m_Ctx.Replay().GetResolver())
{
RDDialog::critical(
this, tr("Not Available"),
tr("Callstack resolution is not available.\n\nCheck remote server connection."));
return;
}
ShowProgressDialog(this, tr("Please Wait - Resolving Symbols"), [this]() {
bool running = true;
m_Ctx.Replay().BlockInvoke(
[&running](IReplayController *r) { running = r->HasCallstacks() && !r->InitResolver(); });
return !running;
float progress = 0.0f;
bool finished = false;
m_Ctx.Replay().AsyncInvoke([this, &progress, &finished](IReplayController *) {
bool success = m_Ctx.Replay().GetResolver()->InitResolver(&progress, NULL);
if(!success)
{
RDDialog::critical(
this, tr("Error loading symbols"),
tr("Couldn't load symbols for callstack resolution.\n\nCheck diagnostic log in "
"Help menu for more details."));
}
finished = true;
});
ShowProgressDialog(this, tr("Resolving symbols, please wait..."),
[&finished]() { return finished; }, [&progress]() { return progress; });
if(m_Ctx.HasAPIInspector())
m_Ctx.GetAPIInspector()->Refresh();
}
+20
View File
@@ -494,3 +494,23 @@ struct EnvironmentModification
};
DECLARE_REFLECTION_STRUCT(EnvironmentModification);
DOCUMENT("The format for a capture file either supported to read from, or export to");
struct CaptureFileFormat
{
DOCUMENT("The name of the format as a single minimal string, e.g. ``rdc``.");
rdcstr name;
DOCUMENT("A human readable description of the file format, e.g. ``RenderDoc native capture``.");
rdcstr description;
DOCUMENT(R"(Indicates whether or not files in this format can be opened and processed as
structured data.
)");
bool openSupported = false;
DOCUMENT("Indicates whether captures or structured data can be saved out in this format.");
bool convertSupported = true;
};
DECLARE_REFLECTION_STRUCT(CaptureFileFormat);
+140 -51
View File
@@ -612,25 +612,6 @@ function must be called from another thread.
DOCUMENT("Notify the interface that the file it has open has been changed on disk.");
virtual void FileChanged() = 0;
DOCUMENT(R"(Query if per-event or per-draw callstacks are available in this capture.
:return: ``True`` if any callstacks are available, ``False`` otherwise.
:rtype: ``bool``
)");
virtual bool HasCallstacks() = 0;
DOCUMENT(R"(Begin initialising a callstack resolver, looking up symbol files and caching as
necessary.
This function will eventually return true if either the resolver successfully initialises, or if it
comes to a point where a problem is encountered that the user cannot solve. That means this can be
used to present a progress dialog and repeatedly queried to see when to allow the user to continue.
:return: ``True`` if any callstacks are available, ``False`` otherwise.
:rtype: ``bool``
)");
virtual bool InitResolver() = 0;
DOCUMENT(R"(Move the replay to reflect the state immediately *after* the given
:data:`EID <APIEvent.eventID>`.
@@ -825,16 +806,6 @@ understanding as well as the type and unit of the resulting information.
)");
virtual rdcarray<BufferDescription> GetBuffers() = 0;
DOCUMENT(R"(Retrieve the list of buffers alive in the capture.
Must only be called after :meth:`InitResolver` has returned ``True``.
:param list callstack: The integer addresses in the original callstack.
:return: The list of resolved callstack entries as strings.
:rtype: ``list`` of ``str``
)");
virtual rdcarray<rdcstr> GetResolve(const rdcarray<uint64_t> &callstack) = 0;
DOCUMENT(R"(Retrieve a list of any newly generated diagnostic messages.
Every time this function is called, any debug messages returned will not be returned again. Only
@@ -1077,6 +1048,48 @@ protected:
~ITargetControl() = default;
};
DOCUMENT(R"(An interface for resolving callstacks. This is separate since it can be either
implemented locally by a file handle, or remotely to an open capture.
)");
struct IStackResolver
{
DOCUMENT(R"(Query if callstacks are available.
:return: ``True`` if any callstacks are available, ``False`` otherwise.
:rtype: ``bool``
)");
virtual bool HasCallstacks() = 0;
DOCUMENT(R"(Begin initialising a callstack resolver, looking up symbol files and caching as
necessary.
This function blocks while trying to initialise callstack resolving, so it should be called on a
separate thread.
:param float progress: A reference to a ``float`` value that will be updated as the init happens
from ``0.0`` to ``1.0``. The parameter can be ``None`` if no progress update is desired.
:param bool killSignal: A reference to a ``bool`` that can be set to ``True`` to stop the lookup
process.
:return: ``True`` if the resolver successfully initialised, ``False`` if something went wrong.
:rtype: ``bool``
)");
virtual bool InitResolver(float *progress, volatile bool *killSignal) = 0;
DOCUMENT(R"(Retrieve the details of each stackframe in the provided callstack.
Must only be called after :meth:`InitResolver` has returned ``True``.
:param list callstack: The integer addresses in the original callstack.
:return: The list of resolved callstack entries as strings.
:rtype: ``list`` of ``str``
)");
virtual rdcarray<rdcstr> GetResolve(const rdcarray<uint64_t> &callstack) = 0;
protected:
IStackResolver() = default;
~IStackResolver() = default;
};
DOCUMENT(R"(A connection to a running remote RenderDoc server on another machine. This allows the
transfer of captures to and from the local machine, as well as remotely replaying a capture with a
local proxy renderer, so that captures that are not supported locally can still be debugged with as
@@ -1086,7 +1099,7 @@ much work as possible happening on the local machine.
No preference for a particular value, see :meth:`DebugPixel`.
)");
struct IRemoteServer
struct IRemoteServer : public IStackResolver
{
DOCUMENT("Closes the connection without affecting the running server.");
virtual void ShutdownConnection() = 0;
@@ -1237,32 +1250,64 @@ protected:
DOCUMENT(R"(A handle to a capture file. Used for simple cheap processing and meta-data fetching
without opening the capture for analysis.
)")
struct ICaptureFile
struct ICaptureFile : public IStackResolver
{
DOCUMENT("Closes the file handle.");
virtual void Shutdown() = 0;
DOCUMENT(R"(Retrieves the status of the handle.
DOCUMENT(R"(Initialises the capture handle from a file.
This returns an error if the capture file used to create the handle wasn't found, or was corrupted,
or something else went wrong while processing it.
This method supports converting from non-native representations via structured data, by specifying
the input format in the :param:`filetype` parameter. The list of supported formats can be retrieved
by calling :meth:`GetCaptureFileFormats`.
:return: The status of the handle to the file.
``rdc`` is guaranteed to always be a supported filetype, and will be assumed if the filetype is
empty or unrecognised.
:param str filename: The filename of the file to open.
:param str filetype: The format of the given file.
:return: The status of the open operation, whether it succeeded or failed (and how it failed).
:rtype: ReplayStatus
)");
virtual ReplayStatus OpenStatus() = 0;
virtual ReplayStatus OpenFile(const char *filename, const char *filetype) = 0;
DOCUMENT(R"(Retrieves the filename used to open this handle.
DOCUMENT(R"(Initialises the file handle from a raw memory buffer.
This filename is exactly as specified without any modificaton to make it an absolute path.
This may be useful if you don't want to parse the whole file or already have the file in memory.
:return: The filename used to create this handle.
:rtype: ``str``
For the :param:`filetype` parameter, see :meth:`OpenFile`.
:param bytes buffer: The buffer containing the data to process.
:param str filetype: The format of the given file.
:return: The status of the open operation, whether it succeeded or failed (and how it failed).
:rtype: ReplayStatus
)");
virtual const char *Filename() = 0;
virtual ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype) = 0;
DOCUMENT(R"(Converts the currently loaded file to a given format and saves it to disk.
This allows converting a native RDC to another representation, or vice-versa converting another
representation back to native RDC.
:param str filename: The filename to save to.
:param str filetype: The format to convert to.
: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) = 0;
DOCUMENT(R"(Returns the list of capture file formats.
:return: The list of capture file formats available.
:rtype: ``list`` of :class:`CaptureFileFormat`
)");
virtual rdcarray<CaptureFileFormat> GetCaptureFileFormats() = 0;
DOCUMENT(R"(Queries for how well a particular capture is supported on the local machine.
If the file was opened with a format other than native ``rdc`` this will always return no
replay support.
:return: How much support for replay exists locally.
:rtype: ReplaySupport
)");
@@ -1282,7 +1327,29 @@ This filename is exactly as specified without any modificaton to make it an abso
)");
virtual const char *RecordedMachineIdent() = 0;
DOCUMENT(R"(Opens a capture for replay locally and returns a handle to the capture.
DOCUMENT(R"(Sets the matadata for this capture handle.
This function may only be called if the handle is 'empty' - i.e. no file has been opened with
:meth:`OpenFile` or :meth:`OpenBuffer`.
.. note:: The only supported values for :param:`thumbType` are :data:`FileType.JPG`,
:data:`FileType.PNG`, :data:`FileType.TGA`, and :data:`FileType.BMP`.
:param str driverName: The name of the driver. Must be a recognised driver name (even if replay
support for that driver is not compiled in locally.
:param int machineIdent: The encoded machine identity value. Optional value and can be left to 0, as
the bits to set are internally defined, so only generally useful if copying a machine ident from
an existing capture.
:param FileType thumbType: The file type of the thumbnail. Ignored if :param:`thumbData` is empty.
:param int thumbWidth: The width of the thumbnail. Ignored if :param:`thumbData` is empty.
:param int thumbHeight: The height of the thumbnail. Ignored if :param:`thumbData` is empty.
:param bytes thumbData: The raw data of the thumbnail. If empty, no thumbnail is set.
)");
virtual void SetMetadata(const char *driverName, uint64_t machineIdent, FileType thumbType,
uint32_t thumbWidth, uint32_t thumbHeight, const bytebuf &thumbData) = 0;
DOCUMENT(R"(Opens a capture for replay locally and returns a handle to the capture. Only supported
for handles opened with a native ``rdc`` capture, otherwise this will fail.
This function will block until the capture is fully loaded and ready.
@@ -1297,8 +1364,33 @@ by the :class:`ReplayController`.
)");
virtual rdcpair<ReplayStatus, IReplayController *> OpenCapture(float *progress) = 0;
DOCUMENT(R"(Returns the structured data for this capture.
The lifetime of this data is scoped to the lifetime of the capture handle, so it cannot be used
after the handle is destroyed.
:return: The structured data representing the file.
:rtype: SDFile
)");
virtual const SDFile &GetStructuredData() = 0;
DOCUMENT(R"(Sets the structured data for this capture.
This allows calling code to populate a capture out of generated structured data. In combination with
:meth:`SetMetadata` this allows a purely in-memory creation of a file to be saved out with
:meth:`Convert`.
The data is copied internally so it can be destroyed after calling this function.
:param SDFile file: The structured data representing the file.
)");
virtual void SetStructuredData(const SDFile &file) = 0;
DOCUMENT(R"(Retrieves the embedded thumbnail from the capture.
.. note:: The only supported values for :param:`type` are :data:`FileType.JPG`,
:data:`FileType.PNG`, :data:`FileType.TGA`, and :data:`FileType.BMP`.
:param FileType type: The image format to convert the thumbnail to.
:param int maxsize: The largest width or height allowed. If the thumbnail is larger, it's resized.
:return: The raw contents of the thumbnail, converted to the desired type at the desired max
@@ -1454,21 +1546,18 @@ extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_VertexOffset(Topology t
uint32_t primitive);
//////////////////////////////////////////////////////////////////////////
// Create a capture handle.
//
// Takes the filename of the log. Always returns a valid handle that must be shut down.
// If any errors happen this can be queried with :meth:`CaptureFile.Status`.
// Create a capture file handle.
//////////////////////////////////////////////////////////////////////////
DOCUMENT(R"(Create a capture handle.
DOCUMENT(R"(Create a handle for a capture file.
Takes the filename of the log. This function *always* returns a valid handle that must be shut down.
If any errors happen this can be queried with :meth:`CaptureFile.Status`.
This function returns a new handle to a capture file. Once initialised by opening a file the handle
can only be shut-down, it is not re-usable.
:return: A handle to the specified path.
:rtype: ReplaySupport
:rtype: ICaptureFile
)");
extern "C" RENDERDOC_API ICaptureFile *RENDERDOC_CC RENDERDOC_OpenCaptureFile(const char *logfile);
extern "C" RENDERDOC_API ICaptureFile *RENDERDOC_CC RENDERDOC_OpenCaptureFile();
//////////////////////////////////////////////////////////////////////////
// Target Control
+206 -175
View File
@@ -28,11 +28,10 @@
#include <algorithm>
#include "api/replay/version.h"
#include "common/common.h"
#include "common/dds_readwrite.h"
#include "hooks/hooks.h"
#include "replay/replay_driver.h"
#include "serialise/rdcfile.h"
#include "serialise/serialiser.h"
#include "stb/stb_image.h"
#include "strings/string_utils.h"
#include "crash_handler.h"
@@ -40,27 +39,25 @@
#include "replay/renderdoc_serialise.inl"
// this one is done by hand as we format it
template <>
std::string DoStringise(const ResourceId &el)
{
char tostrBuf[256] = {0};
RDCCOMPILE_ASSERT(sizeof(el) == sizeof(uint64_t), "ResourceId is no longer 1:1 with uint64_t");
StringFormat::snprintf(tostrBuf, 255, "ResID_%llu", (uint64_t &)el);
return tostrBuf;
return StringFormat::Fmt("ResourceId(%llu)", el);
}
BASIC_TYPE_SERIALISE_STRINGIFY(ResourceId, (uint64_t &)el, SDBasic::UnsignedInteger, 8);
INSTANTIATE_SERIALISE_TYPE(ResourceId);
#if ENABLED(RDOC_LINUX) && ENABLED(RDOC_XLIB)
#include <X11/Xlib.h>
#endif
// from image_viewer.cpp
ReplayStatus IMG_CreateReplayDevice(const char *logfile, IReplayDriver **driver);
// not provided by tinyexr, just do by hand
bool is_exr_file(FILE *f);
ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver);
template <>
std::string DoStringise(const RDCDriver &el)
@@ -82,6 +79,18 @@ std::string DoStringise(const RDCDriver &el)
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const ReplayLogType &el)
{
BEGIN_ENUM_STRINGISE(ReplayLogType);
{
STRINGISE_ENUM_CLASS_NAMED(eReplay_Full, "Full replay including draw");
STRINGISE_ENUM_CLASS_NAMED(eReplay_WithoutDraw, "Replay without draw");
STRINGISE_ENUM_CLASS_NAMED(eReplay_OnlyDraw, "Replay only draw");
}
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const WindowingSystem &el)
{
@@ -144,6 +153,18 @@ std::string DoStringise(const RENDERDOC_InputButton &el)
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const SystemChunk &el)
{
BEGIN_ENUM_STRINGISE(SystemChunk);
{
STRINGISE_ENUM_CLASS_NAMED(DriverInit, "Driver Initialisation Parameters");
STRINGISE_ENUM_CLASS_NAMED(InitialContentsList, "List of Initial Contents Resources");
STRINGISE_ENUM_CLASS_NAMED(InitialContents, "Initial Contents");
}
END_ENUM_STRINGISE();
}
RenderDoc *RenderDoc::m_Inst = NULL;
RenderDoc &RenderDoc::Inst()
@@ -675,16 +696,10 @@ bool RenderDoc::ShouldTriggerCapture(uint32_t frameNumber)
return ret;
}
Serialiser *RenderDoc::OpenWriteSerialiser(uint32_t frameNum, RDCInitParams *params, void *thpixels,
size_t thlen, uint32_t thwidth, uint32_t thheight)
RDCFile *RenderDoc::CreateRDC(uint32_t frameNum, void *thpixels, size_t thlen, uint16_t thwidth,
uint16_t thheight)
{
RDCASSERT(m_CurrentDriver != RDC_Unknown);
#if ENABLED(RDOC_RELEASE)
const bool debugSerialiser = false;
#else
const bool debugSerialiser = true;
#endif
RDCFile *ret = new RDCFile;
m_CurrentLogFile = StringFormat::Fmt("%s_frame%u.rdc", m_LogFile.c_str(), frameNum);
@@ -701,143 +716,29 @@ Serialiser *RenderDoc::OpenWriteSerialiser(uint32_t frameNum, RDCInitParams *par
}
}
Serialiser *fileSerialiser =
new Serialiser(m_CurrentLogFile.c_str(), Serialiser::WRITING, debugSerialiser);
Serialiser *chunkSerialiser = new Serialiser(NULL, Serialiser::WRITING, debugSerialiser);
RDCThumb th;
RDCThumb *thumb = NULL;
if(thpixels)
{
ScopedContext scope(chunkSerialiser, "Thumbnail", THUMBNAIL_DATA, false);
bool HasThumbnail = (thpixels != NULL && thwidth > 0 && thheight > 0);
chunkSerialiser->Serialise("HasThumbnail", HasThumbnail);
if(HasThumbnail)
{
byte *buf = (byte *)thpixels;
chunkSerialiser->Serialise("ThumbWidth", thwidth);
chunkSerialiser->Serialise("ThumbHeight", thheight);
chunkSerialiser->SerialiseBuffer("ThumbnailPixels", buf, thlen);
}
fileSerialiser->Insert(scope.Get(true));
th.len = (uint32_t)thlen;
th.pixels = (const byte *)thpixels;
th.width = thwidth;
th.height = thheight;
thumb = &th;
}
ret->SetData(m_CurrentDriver, m_CurrentDriverName.c_str(), OSUtility::GetMachineIdent(), thumb);
ret->Create(m_CurrentLogFile.c_str());
if(ret->ErrorCode() != ContainerError::NoError)
{
ScopedContext scope(chunkSerialiser, "Capture Create Parameters", CREATE_PARAMS, false);
chunkSerialiser->Serialise("DriverType", m_CurrentDriver);
chunkSerialiser->SerialiseString("DriverName", m_CurrentDriverName);
{
ScopedContext driverparams(chunkSerialiser, "Driver Specific", DRIVER_INIT_PARAMS, false);
params->m_pSerialiser = chunkSerialiser;
params->m_State = WRITING;
params->Serialise();
}
fileSerialiser->Insert(scope.Get(true));
RDCERR("Error creating RDC at '%s'", m_CurrentLogFile.c_str());
SAFE_DELETE(ret);
}
SAFE_DELETE(chunkSerialiser);
return fileSerialiser;
}
ReplayStatus RenderDoc::FillInitParams(const char *logFile, RDCDriver &driverType, string &driverName,
uint64_t &fileMachineIdent, RDCInitParams *params)
{
Serialiser ser(logFile, Serialiser::READING, true);
if(ser.HasError())
{
FILE *f = FileIO::fopen(logFile, "rb");
if(f)
{
int x = 0, y = 0, comp = 0;
int ret = stbi_info_from_file(f, &x, &y, &comp);
FileIO::fseek64(f, 0, SEEK_SET);
if(is_dds_file(f))
ret = x = y = comp = 1;
if(is_exr_file(f))
ret = x = y = comp = 1;
FileIO::fclose(f);
if(ret == 1 && x > 0 && y > 0 && comp > 0)
{
driverType = RDC_Image;
driverName = "Image";
fileMachineIdent = 0;
return ReplayStatus::Succeeded;
}
}
RDCERR("Couldn't open '%s'", logFile);
switch(ser.ErrorCode())
{
case Serialiser::eSerError_FileIO: return ReplayStatus::FileIOFailed;
case Serialiser::eSerError_Corrupt: return ReplayStatus::FileCorrupted;
case Serialiser::eSerError_UnsupportedVersion: return ReplayStatus::FileIncompatibleVersion;
default: break;
}
return ReplayStatus::InternalError;
}
ser.Rewind();
fileMachineIdent = ser.GetSavedMachineIdent();
{
int chunkType = ser.PushContext(NULL, NULL, 1, false);
if(chunkType != THUMBNAIL_DATA)
{
RDCERR("Malformed logfile '%s', first chunk isn't thumbnail data", logFile);
return ReplayStatus::FileCorrupted;
}
ser.SkipCurrentChunk();
ser.PopContext(1);
}
{
int chunkType = ser.PushContext(NULL, NULL, 1, false);
if(chunkType != CREATE_PARAMS)
{
RDCERR("Malformed logfile '%s', second chunk isn't create params", logFile);
return ReplayStatus::FileCorrupted;
}
ser.Serialise("DriverType", driverType);
ser.SerialiseString("DriverName", driverName);
chunkType = ser.PushContext(NULL, NULL, 1, false);
if(chunkType != DRIVER_INIT_PARAMS)
{
RDCERR("Malformed logfile '%s', chunk doesn't contain driver init params", logFile);
return ReplayStatus::FileCorrupted;
}
if(params)
{
params->m_State = READING;
params->m_pSerialiser = &ser;
return params->Serialise();
}
}
// we can just throw away the serialiser, don't need to care about closing/popping contexts
return ReplayStatus::Succeeded;
return ret;
}
bool RenderDoc::HasReplayDriver(RDCDriver driver) const
@@ -883,42 +784,146 @@ void RenderDoc::RegisterRemoteProvider(RDCDriver driver, const char *name,
m_RemoteDriverProviders[driver] = provider;
}
ReplayStatus RenderDoc::CreateReplayDriver(RDCDriver driverType, const char *logfile,
IReplayDriver **driver)
void RenderDoc::RegisterCaptureExporter(const char *filetype, const char *description,
CaptureExporter exporter)
{
if(driver == NULL)
return ReplayStatus::InternalError;
RDCASSERT(m_ImportExportFormats.find(filetype) == m_ImportExportFormats.end());
// allows passing RDC_Unknown as 'I don't care, give me a proxy driver of any type'
// only valid if logfile is NULL and it will be used as a proxy, not to process a log
if(driverType == RDC_Unknown && logfile == NULL && !m_ReplayDriverProviders.empty())
return m_ReplayDriverProviders.begin()->second(logfile, driver);
m_ImportExportFormats[filetype] = description;
// image support is special, handle it here
if(driverType == RDC_Image && logfile != NULL)
return IMG_CreateReplayDevice(logfile, driver);
m_Exporters[filetype] = exporter;
}
if(m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end())
return m_ReplayDriverProviders[driverType](logfile, driver);
void RenderDoc::RegisterCaptureImportExporter(const char *filetype, const char *description,
CaptureImporter importer, CaptureExporter exporter)
{
RDCASSERT(m_ImportExportFormats.find(filetype) == m_ImportExportFormats.end());
RDCERR("Unsupported replay driver requested: %d", driverType);
m_ImportExportFormats[filetype] = description;
m_Importers[filetype] = importer;
m_Exporters[filetype] = exporter;
}
CaptureExporter RenderDoc::GetCaptureExporter(const char *filetype)
{
auto it = m_Exporters.find(filetype);
if(it == m_Exporters.end())
return NULL;
return it->second;
}
CaptureImporter RenderDoc::GetCaptureImporter(const char *filetype)
{
auto it = m_Importers.find(filetype);
if(it == m_Importers.end())
return NULL;
return it->second;
}
std::vector<CaptureFileFormat> RenderDoc::GetCaptureFileFormats()
{
std::vector<CaptureFileFormat> ret;
CaptureFileFormat rdc;
rdc.name = "rdc";
rdc.description = "Native RDC capture file format.";
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;
rdc.openSupported = m_Importers.find(it->first) != m_Importers.end();
rdc.convertSupported = m_Exporters.find(it->first) != m_Exporters.end();
RDCASSERT(rdc.openSupported || rdc.convertSupported);
ret.push_back(fmt);
}
return ret;
}
bool RenderDoc::HasReplaySupport(RDCDriver driverType)
{
if(driverType == RDC_Image)
return true;
if(driverType == RDC_Unknown && !m_ReplayDriverProviders.empty())
return true;
return m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end();
}
ReplayStatus RenderDoc::CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDriver **driver)
{
// passing RDC_Unknown means 'I don't care, give me a proxy driver of any type'
if(proxyDriver == RDC_Unknown)
{
if(!m_ReplayDriverProviders.empty())
return m_ReplayDriverProviders.begin()->second(NULL, driver);
}
if(m_ReplayDriverProviders.find(proxyDriver) != m_ReplayDriverProviders.end())
return m_ReplayDriverProviders[proxyDriver](NULL, driver);
RDCERR("Unsupported replay driver requested: %s", ToStr(proxyDriver).c_str());
return ReplayStatus::APIUnsupported;
}
ReplayStatus RenderDoc::CreateRemoteDriver(RDCDriver driverType, const char *logfile,
IRemoteDriver **driver)
ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, IReplayDriver **driver)
{
if(driver == NULL)
return ReplayStatus::InternalError;
// allows passing NULL rdcfile as 'I don't care, give me a proxy driver of any type'
if(rdc == NULL)
{
if(!m_ReplayDriverProviders.empty())
return m_ReplayDriverProviders.begin()->second(NULL, driver);
RDCERR("Request for proxy replay device, but no replay providers are available.");
return ReplayStatus::InternalError;
}
RDCDriver driverType = rdc->GetDriver();
// image support is special, handle it here
if(driverType == RDC_Image)
return IMG_CreateReplayDevice(rdc, driver);
if(m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end())
return m_ReplayDriverProviders[driverType](rdc, driver);
RDCERR("Unsupported replay driver requested: %s", ToStr(driverType).c_str());
return ReplayStatus::APIUnsupported;
}
ReplayStatus RenderDoc::CreateRemoteDriver(RDCFile *rdc, IRemoteDriver **driver)
{
if(rdc == NULL || driver == NULL)
return ReplayStatus::InternalError;
RDCDriver driverType = rdc->GetDriver();
if(m_RemoteDriverProviders.find(driverType) != m_RemoteDriverProviders.end())
return m_RemoteDriverProviders[driverType](logfile, driver);
return m_RemoteDriverProviders[driverType](rdc, driver);
// replay drivers are remote drivers, fall back and try them
if(m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end())
{
IReplayDriver *dr = NULL;
auto status = m_ReplayDriverProviders[driverType](logfile, &dr);
ReplayStatus status = m_ReplayDriverProviders[driverType](rdc, &dr);
if(status == ReplayStatus::Succeeded)
*driver = (IRemoteDriver *)dr;
@@ -928,7 +933,7 @@ ReplayStatus RenderDoc::CreateRemoteDriver(RDCDriver driverType, const char *log
return status;
}
RDCERR("Unsupported replay driver requested: %d", driverType);
RDCERR("Unsupported replay driver requested: %s", ToStr(driverType).c_str());
return ReplayStatus::APIUnsupported;
}
@@ -1013,14 +1018,40 @@ void RenderDoc::SetProgress(LoadProgressSection section, float delta)
*m_ProgressPtr = progress;
}
void RenderDoc::SuccessfullyWrittenLog(uint32_t frameNumber)
void RenderDoc::FinishCaptureWriting(RDCFile *rdc, uint32_t frameNumber)
{
RDCLOG("Written to disk: %s", m_CurrentLogFile.c_str());
CaptureData cap(m_CurrentLogFile, Timing::GetUnixTimestamp(), frameNumber);
if(rdc)
{
SCOPED_LOCK(m_CaptureLock);
m_Captures.push_back(cap);
// add the resolve database if we were capturing callstacks.
if(m_Options.CaptureCallstacks)
{
SectionProperties props = {};
props.type = SectionType::ResolveDatabase;
props.version = 1;
StreamWriter *w = rdc->WriteSection(props);
size_t sz = 0;
Callstack::GetLoadedModules(NULL, sz);
byte *buf = new byte[sz];
Callstack::GetLoadedModules(buf, sz);
w->Write(buf, sz);
w->Finish();
delete w;
}
delete rdc;
RDCLOG("Written to disk: %s", m_CurrentLogFile.c_str());
CaptureData cap(m_CurrentLogFile, Timing::GetUnixTimestamp(), frameNumber);
{
SCOPED_LOCK(m_CaptureLock);
m_Captures.push_back(cap);
}
}
}
+56 -33
View File
@@ -44,7 +44,6 @@ using std::map;
using std::pair;
using std::set;
class Serialiser;
class Chunk;
// not provided by tinyexr, just do by hand
@@ -85,18 +84,18 @@ enum LogState
WRITING_CAPFRAME,
};
enum SystemChunks
enum class SystemChunk : uint32_t
{
// 0 is reserved as a 'null' chunk that is only for debug
CREATE_PARAMS = 1,
THUMBNAIL_DATA,
DRIVER_INIT_PARAMS,
INITIAL_CONTENTS_LIST,
INITIAL_CONTENTS,
DriverInit = 1,
InitialContentsList,
InitialContents,
FIRST_CHUNK_ID,
FirstDriverChunk = 1000,
};
DECLARE_REFLECTION_ENUM(SystemChunk);
enum RDCDriver
{
RDC_Unknown = 0,
@@ -110,6 +109,7 @@ enum RDCDriver
RDC_Vulkan = 8,
RDC_OpenGLES = 9,
RDC_D3D8 = 10,
RDC_MaxBuiltin,
RDC_Custom = 100000,
RDC_Custom0 = RDC_Custom,
RDC_Custom1,
@@ -141,21 +141,7 @@ enum ReplayLogType
eReplay_OnlyDraw,
};
struct RDCInitParams
{
RDCInitParams()
{
m_State = WRITING;
m_pSerialiser = NULL;
}
virtual ~RDCInitParams() {}
virtual ReplayStatus Serialise() = 0;
LogState m_State;
Serialiser *m_pSerialiser;
Serialiser *GetSerialiser() { return m_pSerialiser; }
};
DECLARE_REFLECTION_ENUM(ReplayLogType);
struct CaptureData
{
@@ -180,8 +166,18 @@ enum LoadProgressSection
class IRemoteDriver;
class IReplayDriver;
typedef ReplayStatus (*RemoteDriverProvider)(const char *logfile, IRemoteDriver **driver);
typedef ReplayStatus (*ReplayDriverProvider)(const char *logfile, IReplayDriver **driver);
class StreamReader;
class RDCFile;
class RDCFile;
typedef ReplayStatus (*RemoteDriverProvider)(RDCFile *rdc, IRemoteDriver **driver);
typedef ReplayStatus (*ReplayDriverProvider)(RDCFile *rdc, IReplayDriver **driver);
typedef ReplayStatus (*CaptureImporter)(const char *filename, StreamReader &reader, RDCFile *rdc,
SDFile &structData);
typedef ReplayStatus (*CaptureExporter)(const char *filename, const RDCFile &rdc,
const SDFile &structData);
typedef bool (*VulkanLayerCheck)(VulkanLayerFlags &flags, std::vector<std::string> &myJSONs,
std::vector<std::string> &otherJSONs);
@@ -224,9 +220,9 @@ public:
void RecreateCrashHandler();
void UnloadCrashHandler();
ICrashHandler *GetCrashHandler() const { return m_ExHandler; }
Serialiser *OpenWriteSerialiser(uint32_t frameNum, RDCInitParams *params, void *thpixels,
size_t thlen, uint32_t thwidth, uint32_t thheight);
void SuccessfullyWrittenLog(uint32_t frameNumber);
RDCFile *CreateRDC(uint32_t frameNum, void *thpixels, size_t thlen, uint16_t thwidth,
uint16_t thheight);
void FinishCaptureWriting(RDCFile *rdc, uint32_t frameNumber);
void AddChildProcess(uint32_t pid, uint32_t ident)
{
@@ -254,12 +250,19 @@ public:
}
}
ReplayStatus FillInitParams(const char *logfile, RDCDriver &driverType, string &driverName,
uint64_t &fileMachineIdent, RDCInitParams *params);
void RegisterReplayProvider(RDCDriver driver, const char *name, ReplayDriverProvider provider);
void RegisterRemoteProvider(RDCDriver driver, const char *name, RemoteDriverProvider provider);
void RegisterCaptureExporter(const char *filetype, const char *description,
CaptureExporter exporter);
void RegisterCaptureImportExporter(const char *filetype, const char *description,
CaptureImporter importer, CaptureExporter exporter);
CaptureExporter GetCaptureExporter(const char *filetype);
CaptureImporter GetCaptureImporter(const char *filetype);
std::vector<CaptureFileFormat> GetCaptureFileFormats();
void SetVulkanLayerCheck(VulkanLayerCheck callback) { m_VulkanCheck = callback; }
void SetVulkanLayerInstall(VulkanLayerInstall callback) { m_VulkanInstall = callback; }
bool NeedVulkanLayerRegistration(VulkanLayerFlags &flags, std::vector<std::string> &myJSONs,
@@ -283,8 +286,11 @@ public:
void SetDarkCheckerboardColor(const Vec4f &col) { m_DarkChecker = col; }
bool IsDarkTheme() { return m_DarkTheme; }
void SetDarkTheme(bool dark) { m_DarkTheme = dark; }
ReplayStatus CreateReplayDriver(RDCDriver driverType, const char *logfile, IReplayDriver **driver);
ReplayStatus CreateRemoteDriver(RDCDriver driverType, const char *logfile, IRemoteDriver **driver);
ReplayStatus CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDriver **driver);
ReplayStatus CreateReplayDriver(RDCFile *rdc, IReplayDriver **driver);
ReplayStatus CreateRemoteDriver(RDCFile *rdc, IRemoteDriver **driver);
bool HasReplaySupport(RDCDriver driverType);
map<RDCDriver, string> GetReplayDrivers();
map<RDCDriver, string> GetRemoteDrivers();
@@ -398,6 +404,10 @@ private:
map<RDCDriver, ReplayDriverProvider> m_ReplayDriverProviders;
map<RDCDriver, RemoteDriverProvider> m_RemoteDriverProviders;
std::map<std::string, std::string> m_ImportExportFormats;
std::map<std::string, CaptureImporter> m_Importers;
std::map<std::string, CaptureExporter> m_Exporters;
VulkanLayerCheck m_VulkanCheck;
VulkanLayerInstall m_VulkanInstall;
@@ -473,3 +483,16 @@ struct DriverRegistration
RenderDoc::Inst().RegisterRemoteProvider(driver, name, provider);
}
};
struct ConversionRegistration
{
ConversionRegistration(const char *filetype, const char *description, CaptureImporter importer,
CaptureExporter exporter)
{
RenderDoc::Inst().RegisterCaptureImportExporter(filetype, description, importer, exporter);
}
ConversionRegistration(const char *filetype, const char *description, CaptureExporter exporter)
{
RenderDoc::Inst().RegisterCaptureExporter(filetype, description, exporter);
}
};
+12 -8
View File
@@ -25,6 +25,7 @@
#include "common/dds_readwrite.h"
#include "core/core.h"
#include "replay/replay_driver.h"
#include "serialise/rdcfile.h"
#include "stb/stb_image.h"
#include "tinyexr/tinyexr.h"
@@ -147,7 +148,7 @@ public:
FrameRecord GetFrameRecord() { return m_FrameRecord; }
const D3D11Pipe::State &GetD3D11PipelineState() { return m_PipelineState; }
// other operations are dropped/ignored, to avoid confusion
void ReadLogInitialisation() {}
void ReadLogInitialisation(RDCFile *rdc) {}
void RenderMesh(uint32_t eventID, const vector<MeshFormat> &secondaryDraws, const MeshDisplay &cfg)
{
}
@@ -202,9 +203,6 @@ public:
{
return "";
}
bool HasCallstacks() { return false; }
void InitCallstackResolver() {}
Callstack::StackResolver *GetCallstackResolver() { return NULL; }
void FreeTargetResource(ResourceId id) {}
vector<PixelModification> PixelHistory(vector<EventUsage> events, ResourceId target, uint32_t x,
uint32_t y, uint32_t slice, uint32_t mip,
@@ -280,9 +278,13 @@ private:
TextureDescription m_TexDetails;
};
ReplayStatus IMG_CreateReplayDevice(const char *logfile, IReplayDriver **driver)
ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver)
{
FILE *f = FileIO::fopen(logfile, "rb");
if(!rdc)
return ReplayStatus::InternalError;
std::string filename;
FILE *f = rdc->StealImageFileHandle(filename);
if(!f)
return ReplayStatus::FileIOFailed;
@@ -382,16 +384,18 @@ ReplayStatus IMG_CreateReplayDevice(const char *logfile, IReplayDriver **driver)
FileIO::fclose(f);
IReplayDriver *proxy = NULL;
auto status = RenderDoc::Inst().CreateReplayDriver(RDC_Unknown, NULL, &proxy);
ReplayStatus status = RenderDoc::Inst().CreateProxyReplayDriver(RDC_Unknown, &proxy);
if(status != ReplayStatus::Succeeded || !proxy)
{
RDCERR("Couldn't create replay driver to proxy-render images");
if(proxy)
proxy->Shutdown();
return status;
}
*driver = new ImageViewer(proxy, logfile);
*driver = new ImageViewer(proxy, filename.c_str());
return ReplayStatus::Succeeded;
}
+4 -4
View File
@@ -704,7 +704,7 @@ void ResourceManagerType::SetInitialChunk(ResourceId id, Chunk *chunk)
auto it = m_InitialChunks.find(id);
RDCASSERT(chunk->GetChunkType() == INITIAL_CONTENTS);
RDCASSERT(chunk->GetChunkType<SystemChunk>() == SystemChunk::InitialContents);
if(it != m_InitialChunks.end())
{
@@ -787,7 +787,7 @@ void ResourceManagerType::Serialise_InitialContentsNeeded(WriteSerialiser &ser)
uint32_t chunkSize = uint32_t(WrittenRecords.size() * sizeof(WrittenRecord) + 16);
SCOPED_SERIALISE_CHUNK(INITIAL_CONTENTS_LIST, chunkSize);
SCOPED_SERIALISE_CHUNK(SystemChunk::InitialContentsList, chunkSize);
SERIALISE_ELEMENT(WrittenRecords);
}
@@ -1039,7 +1039,7 @@ void ResourceManagerType::InsertInitialContentsChunks(WriteSerialiser &ser)
{
uint32_t size = GetSize_InitialState(id, res);
SCOPED_SERIALISE_CHUNK(INITIAL_CONTENTS, size);
SCOPED_SERIALISE_CHUNK(SystemChunk::InitialContents, size);
Serialise_InitialState(ser, id, res);
}
@@ -1068,7 +1068,7 @@ void ResourceManagerType::InsertInitialContentsChunks(WriteSerialiser &ser)
{
uint32_t size = GetSize_InitialState(it->first, it->second);
SCOPED_SERIALISE_CHUNK(INITIAL_CONTENTS, size);
SCOPED_SERIALISE_CHUNK(SystemChunk::InitialContents, size);
Serialise_InitialState(ser, it->first, it->second);
}
+6 -6
View File
@@ -149,8 +149,8 @@ void RenderDoc::TargetControlClientThread(Network::Socket *client)
bytebuf buf;
ICaptureFile *file = RENDERDOC_OpenCaptureFile(captures.back().path.c_str());
if(file->OpenStatus() == ReplayStatus::Succeeded)
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
if(file->OpenFile(captures.back().path.c_str(), "rdc") == ReplayStatus::Succeeded)
{
buf = file->GetThumbnail(FileType::JPG, 0);
}
@@ -196,7 +196,7 @@ void RenderDoc::TargetControlClientThread(Network::Socket *client)
if(client->IsRecvDataWaiting())
{
PacketType type = (PacketType)reader.BeginChunk(0);
PacketType type = reader.ReadChunk<PacketType>();
if(type == ePacket_TriggerCapture)
{
@@ -309,7 +309,7 @@ void RenderDoc::TargetControlServerThread(Network::Socket *sock)
{
ReadSerialiser ser(new StreamReader(client, Ownership::Nothing), Ownership::Stream);
PacketType type = (PacketType)ser.BeginChunk(0);
PacketType type = ser.ReadChunk<PacketType>();
if(type != ePacket_Handshake)
{
@@ -423,7 +423,7 @@ public:
}
}
PacketType type = (PacketType)reader.BeginChunk(0);
PacketType type = reader.ReadChunk<PacketType>();
if(reader.IsErrored())
{
@@ -547,7 +547,7 @@ public:
return msg;
}
PacketType type = (PacketType)reader.BeginChunk(0);
PacketType type = reader.ReadChunk<PacketType>();
if(reader.IsErrored())
{
-15
View File
@@ -1588,21 +1588,6 @@ bool D3D11Replay::IsRenderOutput(ResourceId id)
return false;
}
void D3D11Replay::InitCallstackResolver()
{
m_pDevice->GetSerialiser()->InitCallstackResolver();
}
bool D3D11Replay::HasCallstacks()
{
return m_pDevice->GetSerialiser()->HasCallstacks();
}
Callstack::StackResolver *D3D11Replay::GetCallstackResolver()
{
return m_pDevice->GetSerialiser()->GetCallstackResolver();
}
ResourceId D3D11Replay::CreateProxyTexture(const TextureDescription &templateTex)
{
ResourceId ret;
-4
View File
@@ -165,10 +165,6 @@ public:
bool IsRenderOutput(ResourceId id);
void FileChanged() {}
void InitCallstackResolver();
bool HasCallstacks();
Callstack::StackResolver *GetCallstackResolver();
private:
bool m_WARP;
bool m_Proxy;
+2 -2
View File
@@ -228,10 +228,10 @@ void Init();
Stackwalk *Collect();
Stackwalk *Create();
StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths,
StackResolver *MakeResolver(byte *moduleDB, size_t DBSize, float *progress,
volatile bool *killSignal);
bool GetLoadedModules(char *&buf, size_t &size);
bool GetLoadedModules(byte *buf, size_t &size);
}; // namespace Callstack
namespace FileIO
@@ -66,35 +66,19 @@ Stackwalk *Create()
return new AndroidCallstack(NULL, 0);
}
bool GetLoadedModules(char *&buf, size_t &size)
bool GetLoadedModules(byte *buf, size_t &size)
{
if(buf)
{
buf[0] = 'A';
buf[1] = 'N';
buf[2] = 'R';
buf[3] = 'D';
buf[4] = 'C';
buf[5] = 'A';
buf[6] = 'L';
buf[7] = 'L';
}
memcpy(buf, "ANRDCALL", 8);
size += 8;
return true;
}
class AndroidResolver : public Callstack::StackResolver
{
public:
Callstack::AddressDetails GetAddr(uint64_t addr) { return Callstack::AddressDetails(); }
};
StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths,
volatile bool *killSignal)
StackResolver *MakeResolver(byte *moduleDB, size_t DBSize, float *progress, volatile bool *killSignal)
{
RDCERR("Callstack resolving not supported on Android.");
return new AndroidResolver();
return NULL;
}
};
+4 -20
View File
@@ -66,35 +66,19 @@ Stackwalk *Create()
return new AndroidCallstack(NULL, 0);
}
bool GetLoadedModules(char *&buf, size_t &size)
bool GetLoadedModules(byte *buf, size_t &size)
{
if(buf)
{
buf[0] = 'A';
buf[1] = 'P';
buf[2] = 'P';
buf[3] = 'L';
buf[4] = 'C';
buf[5] = 'A';
buf[6] = 'L';
buf[7] = 'L';
}
memcpy(buf, "APPLCALL", 8);
size += 8;
return true;
}
class AndroidResolver : public Callstack::StackResolver
{
public:
Callstack::AddressDetails GetAddr(uint64_t addr) { return Callstack::AddressDetails(); }
};
StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths,
volatile bool *killSignal)
StackResolver *MakeResolver(byte *moduleDB, size_t DBSize, float *progress, volatile bool *killSignal)
{
RDCERR("Callstack resolving not supported on Apple.");
return new AndroidResolver();
return NULL;
}
};
+21 -24
View File
@@ -114,31 +114,22 @@ Stackwalk *Create()
return new LinuxCallstack(NULL, 0);
}
bool GetLoadedModules(char *&buf, size_t &size)
bool GetLoadedModules(byte *buf, size_t &size)
{
// we just dump the whole file rather than pre-parsing, that way we can improve
// parsing without needing to recapture
FILE *f = FileIO::fopen("/proc/self/maps", "r");
if(buf)
{
buf[0] = 'L';
buf[1] = 'N';
buf[2] = 'U';
buf[3] = 'X';
buf[4] = 'C';
buf[5] = 'A';
buf[6] = 'L';
buf[7] = 'L';
}
memcpy(buf, "LNUXCALL", 8);
size += 8;
char dummy[512];
byte dummy[512];
while(!feof(f))
{
char *readbuf = buf ? buf + size : dummy;
byte *readbuf = buf ? buf + size : dummy;
size += FileIO::fread(readbuf, 1, 512, f);
}
@@ -230,30 +221,33 @@ private:
std::map<uint64_t, Callstack::AddressDetails> m_Cache;
};
StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths,
volatile bool *killSignal)
StackResolver *MakeResolver(byte *moduleDB, size_t DBSize, float *progress, volatile bool *killSignal)
{
// we look in the original locations for the files, we don't prompt if we can't
// find the file, or the file doesn't have symbols (and we don't validate that
// the file is the right version). A good option for doing this would be
// http://github.com/mlabbe/nativefiledialog
bool valid = true;
if(memcmp(moduleDB, "LNUXCALL", 8))
if(DBSize < 8 || memcmp(moduleDB, "LNUXCALL", 8))
{
RDCWARN("Can't load callstack resolve for this log. Possibly from another platform?");
valid = false;
return NULL;
}
char *search = moduleDB + 8;
char *start = (char *)(moduleDB + 8);
char *search = start;
char *dbend = (char *)(moduleDB + DBSize);
vector<LookupModule> modules;
while(valid && search && size_t(search - moduleDB) < DBSize)
while(search && search < dbend)
{
if(killSignal && *killSignal)
break;
if(progress)
*progress = float(search - start) / float(DBSize);
// find .text segments
{
long unsigned int base = 0, end = 0;
@@ -273,10 +267,10 @@ StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths
mod.end = (uint64_t)end;
search += offs;
while(size_t(search - moduleDB) < DBSize && (*search == ' ' || *search == '\t'))
while(search < dbend && (*search == ' ' || *search == '\t'))
search++;
if(size_t(search - moduleDB) < DBSize && *search != '[' && *search != 0 && *search != '\n')
if(search < dbend && *search != '[' && *search != 0 && *search != '\n')
{
size_t n = ARRAY_COUNT(mod.path) - 1;
mod.path[n] = 0;
@@ -287,7 +281,7 @@ StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths
mod.path[i] = 0;
break;
}
if(search + i >= moduleDB + DBSize)
if(search + i >= dbend)
{
mod.path[i] = 0;
break;
@@ -330,7 +324,10 @@ StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths
}
}
if(search >= (char *)(moduleDB + DBSize))
if(progress)
*progress = RDCMIN(1.0f, float(search - start) / float(DBSize));
if(search >= dbend)
break;
search = strchr(search, '\n');
+21 -28
View File
@@ -369,8 +369,7 @@ private:
class Win32CallstackResolver : public Callstack::StackResolver
{
public:
Win32CallstackResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths,
volatile bool *killSignal);
Win32CallstackResolver(byte *moduleDB, size_t DBSize, float *progress, volatile bool *killSignal);
~Win32CallstackResolver();
Callstack::AddressDetails GetAddr(uint64_t addr);
@@ -519,7 +518,7 @@ struct CV_INFO_PDB70
struct EnumBuf
{
char *bufPtr;
byte *bufPtr;
size_t size;
};
@@ -676,7 +675,7 @@ wstring Win32CallstackResolver::pdbBrowse(wstring startingPoint)
return outBuf;
}
Win32CallstackResolver::Win32CallstackResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths,
Win32CallstackResolver::Win32CallstackResolver(byte *moduleDB, size_t DBSize, float *progress,
volatile bool *killSignal)
{
wstring configPath = StringFormat::UTF82Wide(FileIO::GetAppFolderFilename("config.ini"));
@@ -711,18 +710,8 @@ Win32CallstackResolver::Win32CallstackResolver(char *moduleDB, size_t DBSize, st
split(ignores, pdbIgnores, L';');
wstring widepdbsearch = StringFormat::UTF82Wide(pdbSearchPaths);
split(widepdbsearch, pdbRememberedPaths, L';');
if(memcmp(moduleDB, "WN32CALL", 8))
{
RDCWARN("Can't load callstack resolve for this log. Possibly from another platform?");
return;
}
char *chunks = moduleDB + 8;
char *end = chunks + DBSize - 8;
byte *chunks = moduleDB + 8;
byte *end = chunks + DBSize - 8;
EnumModChunk *chunk = (EnumModChunk *)(chunks);
WCHAR *modName = (WCHAR *)(chunks + sizeof(EnumModChunk));
@@ -733,6 +722,9 @@ Win32CallstackResolver::Win32CallstackResolver(char *moduleDB, size_t DBSize, st
chunk = (EnumModChunk *)chunks;
modName = (WCHAR *)(chunks + sizeof(EnumModChunk));
if(progress)
*progress = float(chunks - moduleDB) / float(end - moduleDB);
if(killSignal && *killSignal)
break;
@@ -862,6 +854,9 @@ Win32CallstackResolver::Win32CallstackResolver(char *moduleDB, size_t DBSize, st
continue;
}
if(progress)
*progress = RDCMIN(1.0f, float(chunks - moduleDB) / float(end - moduleDB));
DIA2::SetBaseAddress(m.moduleId, chunk->base);
RDCLOG("Loaded Symbols for %ls", m.name.c_str());
@@ -971,13 +966,18 @@ Stackwalk *Create()
return new Win32Callstack(NULL, 0);
}
StackResolver *MakeResolver(char *moduleDB, size_t DBSize, string pdbSearchPaths,
volatile bool *killSignal)
StackResolver *MakeResolver(byte *moduleDB, size_t DBSize, float *progress, volatile bool *killSignal)
{
return new Win32CallstackResolver(moduleDB, DBSize, pdbSearchPaths, killSignal);
if(DBSize < 8 || memcmp(moduleDB, "WN32CALL", 8))
{
RDCWARN("Can't load callstack resolve for this log. Possibly from another platform?");
return NULL;
}
return new Win32CallstackResolver(moduleDB, DBSize, progress, killSignal);
}
bool GetLoadedModules(char *&buf, size_t &size)
bool GetLoadedModules(byte *buf, size_t &size)
{
EnumBuf e;
e.bufPtr = buf;
@@ -985,14 +985,7 @@ bool GetLoadedModules(char *&buf, size_t &size)
if(buf)
{
buf[0] = 'W';
buf[1] = 'N';
buf[2] = '3';
buf[3] = '2';
buf[4] = 'C';
buf[5] = 'A';
buf[6] = 'L';
buf[7] = 'L';
memcpy(buf, "WN32CALL", 8);
e.bufPtr += 8;
}
+445 -57
View File
@@ -27,7 +27,9 @@
#include "jpeg-compressor/jpgd.h"
#include "jpeg-compressor/jpge.h"
#include "replay/replay_controller.h"
#include "serialise/rdcfile.h"
#include "serialise/serialiser.h"
#include "stb/stb_image.h"
#include "stb/stb_image_resize.h"
#include "stb/stb_image_write.h"
@@ -39,70 +41,263 @@ static void writeToByteVector(void *context, void *data, int size)
vec->insert(vec->end(), start, end);
}
static RDCDriver driverFromName(const char *driverName)
{
for(int d = RDC_Unknown; d < RDC_MaxBuiltin; d++)
{
if(driverName == ToStr((RDCDriver)d))
return (RDCDriver)d;
}
return RDC_Unknown;
}
static RDCThumb convertThumb(FileType thumbType, uint32_t thumbWidth, uint32_t thumbHeight,
const bytebuf &thumbData)
{
RDCThumb ret;
if(thumbWidth > 0xffff || thumbHeight > 0xffff)
return ret;
ret.width = thumbWidth & 0xffff;
ret.height = thumbHeight & 0xffff;
byte *decoded = NULL;
if(thumbType == FileType::JPG)
{
// just need to copy
byte *pixels = (byte *)malloc(thumbData.size());
memcpy(pixels, thumbData.data(), thumbData.size());
ret.pixels = pixels;
ret.len = (uint32_t)thumbData.size();
}
else
{
int ignore = 0;
decoded =
stbi_load_from_memory(thumbData.data(), thumbData.count(), &ignore, &ignore, &ignore, 3);
if(decoded == NULL)
{
RDCERR("Couldn't decode provided thumbnail");
return ret;
}
}
if(decoded)
{
int len = ret.width * ret.height * 3;
byte *pixels = (byte *)malloc(len);
jpge::params p;
p.m_quality = 90;
jpge::compress_image_to_jpeg_file_in_memory(pixels, len, (int)ret.width, (int)ret.height, 3,
decoded, p);
ret.pixels = pixels;
ret.len = (uint32_t)len;
}
return ret;
}
class CaptureFile : public ICaptureFile
{
public:
CaptureFile(const char *f);
virtual ~CaptureFile() {}
CaptureFile();
virtual ~CaptureFile();
ReplayStatus OpenFile(const char *filename, const char *filetype);
ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype);
void Shutdown() { delete this; }
ReplayStatus OpenStatus() { return m_Status; }
const char *Filename() { return m_Filename.c_str(); }
ReplaySupport LocalReplaySupport() { return m_Support; }
const char *DriverName() { return m_DriverName.c_str(); }
const char *RecordedMachineIdent() { return m_Ident.c_str(); }
rdcpair<ReplayStatus, IReplayController *> OpenCapture(float *progress);
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);
rdcarray<CaptureFileFormat> GetCaptureFileFormats()
{
return RenderDoc::Inst().GetCaptureFileFormats();
}
const SDFile &GetStructuredData()
{
// TODO - fetch structured data from capture on demand?
return m_StructuredData;
}
void SetStructuredData(const SDFile &file)
{
m_StructuredData.version = file.version;
m_StructuredData.chunks.reserve(file.chunks.size());
for(SDChunk *obj : file.chunks)
m_StructuredData.chunks.push_back(obj->Duplicate());
m_StructuredData.buffers.reserve(file.buffers.size());
for(bytebuf *buf : file.buffers)
m_StructuredData.buffers.push_back(new bytebuf(*buf));
}
bytebuf GetThumbnail(FileType type, uint32_t maxsize);
bool HasCallstacks();
bool InitResolver(float *progress, volatile bool *killSignal);
rdcarray<rdcstr> GetResolve(const rdcarray<uint64_t> &callstack);
private:
std::string m_Filename, m_DriverName, m_Ident;
RDCDriver m_DriverType;
ReplayStatus m_Status;
ReplaySupport m_Support;
ReplayStatus Init();
RDCFile *m_RDC = NULL;
Callstack::StackResolver *m_Resolver = NULL;
SDFile m_StructuredData;
std::string m_DriverName, m_Ident;
ReplaySupport m_Support = ReplaySupport::Unsupported;
};
CaptureFile::CaptureFile(const char *f)
CaptureFile::CaptureFile()
{
m_Filename = f;
}
m_DriverType = RDC_Unknown;
uint64_t fileMachineIdent = 0;
m_Status = RenderDoc::Inst().FillInitParams(Filename(), m_DriverType, m_DriverName,
fileMachineIdent, NULL);
CaptureFile::~CaptureFile()
{
SAFE_DELETE(m_RDC);
SAFE_DELETE(m_Resolver);
}
if(m_Status != ReplayStatus::Succeeded)
ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype)
{
CaptureImporter importer = RenderDoc::Inst().GetCaptureImporter(filetype);
if(importer)
{
m_Support = ReplaySupport::Unsupported;
ReplayStatus ret;
{
StreamReader reader(FileIO::fopen(filename, "rb"));
m_RDC = new RDCFile;
ret = importer(filename, reader, m_RDC, m_StructuredData);
}
if(ret != ReplayStatus::Succeeded)
{
delete m_RDC;
return ret;
}
}
else
{
m_Support = RenderDoc::Inst().HasReplayDriver(m_DriverType) ? ReplaySupport::Supported
: ReplaySupport::Unsupported;
if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc"))
RDCWARN("Opening file with unrecognised filetype '%s' - treating as 'rdc'", filetype);
m_RDC = new RDCFile;
m_RDC->Open(filename);
}
return Init();
}
ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype)
{
CaptureImporter importer = RenderDoc::Inst().GetCaptureImporter(filetype);
std::vector<byte> vec(buffer.begin(), buffer.end());
if(importer)
{
ReplayStatus ret;
if(fileMachineIdent != 0)
{
uint64_t machineIdent = OSUtility::GetMachineIdent();
StreamReader reader(vec);
m_RDC = new RDCFile;
ret = importer(NULL, reader, m_RDC, m_StructuredData);
}
m_Ident = OSUtility::MakeMachineIdentString(fileMachineIdent);
if((machineIdent & OSUtility::MachineIdent_OS_Mask) !=
(fileMachineIdent & OSUtility::MachineIdent_OS_Mask))
m_Support = ReplaySupport::SuggestRemote;
if(ret != ReplayStatus::Succeeded)
{
delete m_RDC;
return ret;
}
}
else
{
if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc"))
RDCWARN("Opening file with unrecognised filetype '%s' - treating as 'rdc'", filetype);
m_RDC = new RDCFile;
m_RDC->Open(vec);
}
return Init();
}
ReplayStatus CaptureFile::Init()
{
if(!m_RDC)
return ReplayStatus::InternalError;
switch(m_RDC->ErrorCode())
{
case ContainerError::FileNotFound: return ReplayStatus::FileNotFound; break;
case ContainerError::FileIO: return ReplayStatus::FileIOFailed; break;
case ContainerError::Corrupt: return ReplayStatus::FileCorrupted; break;
case ContainerError::UnsupportedVersion: return ReplayStatus::FileIncompatibleVersion; break;
case ContainerError::NoError:
{
RDCDriver driverType = m_RDC->GetDriver();
m_DriverName = m_RDC->GetDriverName();
uint64_t fileMachineIdent = m_RDC->GetMachineIdent();
m_Support = RenderDoc::Inst().HasReplayDriver(driverType) ? ReplaySupport::Supported
: ReplaySupport::Unsupported;
if(fileMachineIdent != 0)
{
uint64_t machineIdent = OSUtility::GetMachineIdent();
m_Ident = OSUtility::MakeMachineIdentString(fileMachineIdent);
if((machineIdent & OSUtility::MachineIdent_OS_Mask) !=
(fileMachineIdent & OSUtility::MachineIdent_OS_Mask))
m_Support = ReplaySupport::SuggestRemote;
}
// can't open files without a capture in them (except images, which are special)
if(driverType != RDC_Image && m_RDC->SectionIndex(SectionType::FrameCapture) == -1)
m_Support = ReplaySupport::Unsupported;
return ReplayStatus::Succeeded;
}
}
// all container errors should be handled and returned above
return ReplayStatus::InternalError;
}
rdcpair<ReplayStatus, IReplayController *> CaptureFile::OpenCapture(float *progress)
{
if(m_Status != ReplayStatus::Succeeded)
return make_rdcpair<ReplayStatus, IReplayController *>(m_Status, NULL);
if(!m_RDC || m_RDC->ErrorCode() != ContainerError::NoError)
return make_rdcpair<ReplayStatus, IReplayController *>(ReplayStatus::InternalError, NULL);
ReplayController *render = new ReplayController();
ReplayStatus ret;
RenderDoc::Inst().SetProgressPtr(progress);
ret = render->CreateDevice(Filename());
ret = render->CreateDevice(m_RDC);
RenderDoc::Inst().SetProgressPtr(NULL);
@@ -112,36 +307,164 @@ rdcpair<ReplayStatus, IReplayController *> CaptureFile::OpenCapture(float *progr
return make_rdcpair<ReplayStatus, IReplayController *>(ret, render);
}
void CaptureFile::SetMetadata(const char *driverName, uint64_t machineIdent, FileType thumbType,
uint32_t thumbWidth, uint32_t thumbHeight, const bytebuf &thumbData)
{
if(m_RDC)
{
RDCERR("Cannot set metadata on file that's already opened.");
return;
}
RDCThumb *thumb = NULL;
RDCThumb th;
if(!thumbData.empty())
{
th = convertThumb(thumbType, thumbWidth, thumbHeight, thumbData);
thumb = &th;
}
RDCDriver driver = driverFromName(driverName);
if(driver == RDC_Unknown)
{
RDCERR("Unrecognised driver name '%s'.", driverName);
return;
}
m_RDC = new RDCFile;
m_RDC->SetData(driver, driverName, machineIdent, thumb);
free((void *)th.pixels);
}
ReplayStatus CaptureFile::Convert(const char *filename, const char *filetype)
{
if(!m_RDC)
{
RDCERR("Data missing for creation of file, set metadata first.");
return ReplayStatus::FileCorrupted;
}
CaptureExporter exporter = RenderDoc::Inst().GetCaptureExporter(filetype);
if(exporter)
return exporter(filename, *m_RDC, GetStructuredData());
if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc"))
RDCWARN("Converting file to unrecognised filetype '%s' - treating as 'rdc'", filetype);
RDCFile output;
output.SetData(m_RDC->GetDriver(), m_RDC->GetDriverName().c_str(), m_RDC->GetMachineIdent(),
&m_RDC->GetThumbnail());
output.Create(filename);
if(output.ErrorCode() != ContainerError::NoError)
{
switch(output.ErrorCode())
{
case ContainerError::FileNotFound: return ReplayStatus::FileNotFound; break;
case ContainerError::FileIO: return ReplayStatus::FileIOFailed; break;
default: break;
}
return ReplayStatus::InternalError;
}
bool success = true;
// when we don't have a frame capture section, write it from the structured data.
int frameCaptureIndex = m_RDC->SectionIndex(SectionType::FrameCapture);
if(frameCaptureIndex == -1)
{
SectionProperties frameCapture;
frameCapture.flags = SectionFlags::ZstdCompressed;
frameCapture.type = SectionType::FrameCapture;
frameCapture.name = SectionTypeNames[uint32_t(frameCapture.type)];
frameCapture.version = GetStructuredData().version;
StreamWriter *writer = output.WriteSection(frameCapture);
WriteSerialiser ser(writer, Ownership::Nothing);
ser.WriteStructuredFile(GetStructuredData());
writer->Finish();
success = success && !writer->IsErrored();
delete writer;
}
else
{
// otherwise write it straight, but compress it to zstd
SectionProperties props = m_RDC->GetSectionProperties(frameCaptureIndex);
props.flags = SectionFlags::ZstdCompressed;
StreamWriter *writer = output.WriteSection(props);
StreamReader *reader = m_RDC->ReadSection(frameCaptureIndex);
StreamTransfer(writer, reader, NULL);
writer->Finish();
success = success && !writer->IsErrored() && !reader->IsErrored();
delete reader;
delete writer;
if(!success)
return ReplayStatus::FileIOFailed;
}
if(!success)
return ReplayStatus::FileIOFailed;
// write all other sections
for(int i = 0; i < m_RDC->NumSections(); i++)
{
const SectionProperties &props = m_RDC->GetSectionProperties(i);
if(props.type == SectionType::FrameCapture)
continue;
StreamWriter *writer = output.WriteSection(props);
StreamReader *reader = m_RDC->ReadSection(i);
StreamTransfer(writer, reader, NULL);
writer->Finish();
success = success && !writer->IsErrored() && !reader->IsErrored();
delete reader;
delete writer;
if(!success)
return ReplayStatus::FileIOFailed;
}
if(!success)
return ReplayStatus::FileIOFailed;
return ReplayStatus::Succeeded;
}
bytebuf CaptureFile::GetThumbnail(FileType type, uint32_t maxsize)
{
bytebuf buf;
Serialiser ser(Filename(), Serialiser::READING, false);
if(ser.HasError())
if(m_RDC == NULL)
return buf;
ser.Rewind();
const RDCThumb &thumb = m_RDC->GetThumbnail();
int chunkType = ser.PushContext(NULL, NULL, 1, false);
if(chunkType != THUMBNAIL_DATA)
return buf;
bool HasThumbnail = false;
ser.Serialise(NULL, HasThumbnail);
if(!HasThumbnail)
return buf;
byte *jpgbuf = NULL;
size_t thumblen = 0;
uint32_t thumbwidth = 0, thumbheight = 0;
{
ser.Serialise("ThumbWidth", thumbwidth);
ser.Serialise("ThumbHeight", thumbheight);
ser.SerialiseBuffer("ThumbnailPixels", jpgbuf, thumblen);
}
const byte *jpgbuf = thumb.pixels;
size_t thumblen = thumb.len;
uint32_t thumbwidth = thumb.width, thumbheight = thumb.height;
if(jpgbuf == NULL)
return buf;
@@ -228,7 +551,6 @@ bytebuf CaptureFile::GetThumbnail(FileType type, uint32_t maxsize)
{
RDCERR("Unsupported file type %d in thumbnail fetch", type);
free(thumbpixels);
delete[] jpgbuf;
return buf;
}
}
@@ -238,12 +560,78 @@ bytebuf CaptureFile::GetThumbnail(FileType type, uint32_t maxsize)
free(thumbpixels);
}
delete[] jpgbuf;
return buf;
}
extern "C" RENDERDOC_API ICaptureFile *RENDERDOC_CC RENDERDOC_OpenCaptureFile(const char *logfile)
bool CaptureFile::HasCallstacks()
{
return new CaptureFile(logfile);
}
return m_RDC && m_RDC->SectionIndex(SectionType::ResolveDatabase) >= 0;
}
bool CaptureFile::InitResolver(float *progress, volatile bool *killSignal)
{
if(!HasCallstacks())
{
RDCERR("Capture has no callstacks - can't initialise resolver.");
return false;
}
if(progress)
*progress = 0.001f;
int idx = m_RDC->SectionIndex(SectionType::ResolveDatabase);
StreamReader *reader = m_RDC->ReadSection(idx);
std::vector<byte> buf;
buf.resize((size_t)reader->GetSize());
bool success = reader->Read(buf.data(), reader->GetSize());
delete reader;
if(!success)
{
RDCERR("Failed to read resolve database.");
return false;
}
if(progress)
*progress = 0.002f;
m_Resolver = Callstack::MakeResolver(buf.data(), buf.size(), progress, killSignal);
if(!m_Resolver)
{
RDCERR("Couldn't create callstack resolver - capture possibly from another platform.");
return false;
}
return true;
}
rdcarray<rdcstr> CaptureFile::GetResolve(const rdcarray<uint64_t> &callstack)
{
rdcarray<rdcstr> ret;
if(callstack.empty())
return ret;
if(!m_Resolver)
{
ret = {""};
return ret;
}
ret.reserve(callstack.size());
for(uint64_t frame : callstack)
{
Callstack::AddressDetails info = m_Resolver->GetAddr(frame);
ret.push_back(info.formattedString());
}
return ret;
}
extern "C" RENDERDOC_API ICaptureFile *RENDERDOC_CC RENDERDOC_OpenCaptureFile()
{
return new CaptureFile();
}
-54
View File
@@ -265,44 +265,6 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_TriggerExceptionHandler(voi
}
}
extern "C" RENDERDOC_API ReplaySupport RENDERDOC_CC
RENDERDOC_SupportLocalReplay(const char *logfile, rdcstr *driver, rdcstr *recordMachineIdent)
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile(logfile);
if(driver)
*driver = file->DriverName();
if(recordMachineIdent)
*recordMachineIdent = file->RecordedMachineIdent();
ReplaySupport support = file->LocalReplaySupport();
file->Shutdown();
return support;
}
extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC
RENDERDOC_CreateReplayRenderer(const char *logfile, float *progress, IReplayController **rend)
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile(logfile);
ReplayStatus ret = file->OpenStatus();
if(ret != ReplayStatus::Succeeded)
{
file->Shutdown();
return ret;
}
std::tie(ret, *rend) = file->OpenCapture(progress);
file->Shutdown();
return ret;
}
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC
RENDERDOC_ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine,
const rdcarray<EnvironmentModification> &env, const char *logfile,
@@ -346,22 +308,6 @@ RENDERDOC_InjectIntoProcess(uint32_t pid, const rdcarray<EnvironmentModification
return Process::InjectIntoProcess(pid, env, logfile, opts, waitForExit != 0);
}
extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_GetThumbnail(const char *filename, FileType type,
uint32_t maxsize, bytebuf *buf)
{
ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename);
if(file->OpenStatus() != ReplayStatus::Succeeded)
{
file->Shutdown();
return false;
}
*buf = file->GetThumbnail(type, maxsize);
file->Shutdown();
return true;
}
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_FreeArrayMem(const void *mem)
{
free((void *)mem);
+7 -56
View File
@@ -31,6 +31,7 @@
#include "jpeg-compressor/jpge.h"
#include "maths/formatpacking.h"
#include "os/os_specific.h"
#include "serialise/rdcfile.h"
#include "serialise/serialiser.h"
#include "stb/stb_image.h"
#include "stb/stb_image_write.h"
@@ -321,31 +322,6 @@ rdcarray<TextureDescription> ReplayController::GetTextures()
return m_Textures;
}
rdcarray<rdcstr> ReplayController::GetResolve(const rdcarray<uint64_t> &callstack)
{
rdcarray<rdcstr> ret;
if(callstack.empty())
return ret;
Callstack::StackResolver *resolv = m_pDevice->GetCallstackResolver();
if(resolv == NULL)
{
ret = {""};
return ret;
}
ret.reserve(callstack.size());
for(uint64_t frame : callstack)
{
Callstack::AddressDetails info = resolv->GetAddr(frame);
ret.push_back(info.formattedString());
}
return ret;
}
rdcarray<DebugMessage> ReplayController::GetDebugMessages()
{
return m_pDevice->GetDebugMessages();
@@ -1549,29 +1525,15 @@ void ReplayController::RemoveReplacement(ResourceId id)
m_Outputs[i]->Display();
}
ReplayStatus ReplayController::CreateDevice(const char *logfile)
ReplayStatus ReplayController::CreateDevice(RDCFile *rdc)
{
RDCLOG("Creating replay device for %s", logfile);
RDCDriver driverType = RDC_Unknown;
string driverName = "";
uint64_t fileMachineIdent = 0;
auto status =
RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, fileMachineIdent, NULL);
if(driverType == RDC_Unknown || driverName == "" || status != ReplayStatus::Succeeded)
{
RDCERR("Couldn't get device type from log");
return status;
}
IReplayDriver *driver = NULL;
status = RenderDoc::Inst().CreateReplayDriver(driverType, logfile, &driver);
ReplayStatus status = RenderDoc::Inst().CreateReplayDriver(rdc, &driver);
if(driver && status == ReplayStatus::Succeeded)
{
RDCLOG("Created replay driver.");
return PostCreateInit(driver);
return PostCreateInit(driver, rdc);
}
RDCERR("Couldn't create a replay device :(.");
@@ -1583,18 +1545,18 @@ ReplayStatus ReplayController::SetDevice(IReplayDriver *device)
if(device)
{
RDCLOG("Got replay driver.");
return PostCreateInit(device);
return PostCreateInit(device, NULL);
}
RDCERR("Given invalid replay driver.");
return ReplayStatus::InternalError;
}
ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device)
ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rdc)
{
m_pDevice = device;
m_pDevice->ReadLogInitialisation();
m_pDevice->ReadLogInitialisation(rdc);
FetchPipelineState();
@@ -1611,22 +1573,11 @@ void ReplayController::FileChanged()
m_pDevice->FileChanged();
}
bool ReplayController::HasCallstacks()
{
return m_pDevice->HasCallstacks();
}
APIProperties ReplayController::GetAPIProperties()
{
return m_pDevice->GetAPIProperties();
}
bool ReplayController::InitResolver()
{
m_pDevice->InitCallstackResolver();
return m_pDevice->GetCallstackResolver() != NULL;
}
void ReplayController::FetchPipelineState()
{
m_pDevice->SavePipelineState();
+2 -6
View File
@@ -126,14 +126,11 @@ public:
APIProperties GetAPIProperties();
ReplayStatus CreateDevice(const char *logfile);
ReplayStatus CreateDevice(RDCFile *rdc);
ReplayStatus SetDevice(IReplayDriver *device);
void FileChanged();
bool HasCallstacks();
bool InitResolver();
void SetFrameEvent(uint32_t eventID, bool force);
void FetchPipelineState();
@@ -165,7 +162,6 @@ public:
CounterDescription DescribeCounter(GPUCounter counterID);
rdcarray<TextureDescription> GetTextures();
rdcarray<BufferDescription> GetBuffers();
rdcarray<rdcstr> GetResolve(const rdcarray<uint64_t> &callstack);
rdcarray<DebugMessage> GetDebugMessages();
rdcarray<PixelModification> PixelHistory(ResourceId target, uint32_t x, uint32_t y, uint32_t slice,
@@ -200,7 +196,7 @@ public:
void Shutdown();
private:
ReplayStatus PostCreateInit(IReplayDriver *device);
ReplayStatus PostCreateInit(IReplayDriver *device, RDCFile *rdc);
DrawcallDescription *GetDrawcallByEID(uint32_t eventID);
+3 -5
View File
@@ -65,6 +65,8 @@ struct GetTextureDataParams
}
};
class RDCFile;
// these two interfaces define what an API driver implementation must provide
// to the replay. At minimum it must implement IRemoteDriver which contains
// all of the functionality that cannot be achieved elsewhere. An IReplayDriver
@@ -106,7 +108,7 @@ public:
virtual FrameRecord GetFrameRecord() = 0;
virtual void ReadLogInitialisation() = 0;
virtual void ReadLogInitialisation(RDCFile *rdc) = 0;
virtual void ReplayLog(uint32_t endEventID, ReplayLogType replayType) = 0;
virtual vector<uint32_t> GetPassEvents(uint32_t eventID) = 0;
@@ -153,10 +155,6 @@ public:
virtual void FileChanged() = 0;
virtual void InitCallstackResolver() = 0;
virtual bool HasCallstacks() = 0;
virtual Callstack::StackResolver *GetCallstackResolver() = 0;
virtual bool NeedRemapForFetch(const ResourceFormat &format) = 0;
};
+7
View File
@@ -763,3 +763,10 @@ ReplayStatus exportXMLZ(const char *filename, const RDCFile &rdc, const SDFile &
return Structured2XML(filename, rdc, structData.version, structData.chunks);
}
static ConversionRegistration XMLConversionRegistration("xml", R"(XML+ZIP format.
Stores the structured data in an xml tree, with large buffer data stored in indexed blobs in
similarly named zip file.
)",
&importXMLZ, &exportXMLZ);
+4 -4
View File
@@ -292,8 +292,8 @@ struct ThumbCommand : public Command
bytebuf buf;
ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename.c_str());
ReplayStatus st = file->OpenStatus();
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
ReplayStatus st = file->OpenFile(filename.c_str(), "rdc");
if(st == ReplayStatus::Succeeded)
{
buf = file->GetThumbnail(type, maxsize);
@@ -587,9 +587,9 @@ struct ReplayCommand : public Command
{
std::cout << "Replaying '" << filename << "' locally.." << std::endl;
ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename.c_str());
ICaptureFile *file = RENDERDOC_OpenCaptureFile();
if(file->OpenStatus() != ReplayStatus::Succeeded)
if(file->OpenFile(filename.c_str(), "rdc") != ReplayStatus::Succeeded)
{
std::cerr << "Couldn't load '" << filename << "'." << std::endl;
return 1;