mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-22 14:36:35 +00:00
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:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user