diff --git a/docs/python_api/enums_data.rst b/docs/python_api/enums_data.rst index 2f6fbd435..b96c8523e 100644 --- a/docs/python_api/enums_data.rst +++ b/docs/python_api/enums_data.rst @@ -5,4 +5,4 @@ Enums and Data Structures :members: :undoc-members: :imported-members: - :exclude-members: free_functions__, enum_constants__, name_match__startswith__D3D11_, name_match__startswith__D3D12_, name_match__startswith__VK_, name_match__startswith__GL_, str, ReplayRenderer, ReplayOutput, TargetControl, RemoteServer + :exclude-members: free_functions__, enum_constants__, name_match__startswith__D3D11_, name_match__startswith__D3D12_, name_match__startswith__VK_, name_match__startswith__GL_, str, ReplayRenderer, ReplayOutput, TargetControl, RemoteServer, CaptureFile diff --git a/docs/python_api/funcs.rst b/docs/python_api/funcs.rst index 47f6438a2..e286b5b3a 100644 --- a/docs/python_api/funcs.rst +++ b/docs/python_api/funcs.rst @@ -1,33 +1,54 @@ Functions ========= +.. contents:: + .. module:: renderdoc +Capture File Access +------------------- + +.. autofunction:: renderdoc.OpenCaptureFile + +Target Control +-------------- + +.. autofunction:: renderdoc.EnumerateRemoteTargets +.. autofunction:: renderdoc.CreateTargetControl + +Remote Servers +-------------- + +.. autofunction:: renderdoc.CreateRemoteServerConnection +.. autofunction:: renderdoc.GetDefaultRemoteServerPort +.. autofunction:: renderdoc.BecomeRemoteServer + +Local Execution & Injection +--------------------------- + +.. autofunction:: renderdoc.GetDefaultCaptureOptions +.. autofunction:: renderdoc.ExecuteAndInject +.. autofunction:: renderdoc.InjectIntoProcess +.. autofunction:: renderdoc.StartGlobalHook + +Logging & Versioning +-------------------- + +.. autofunction:: renderdoc.SetDebugLogFile +.. autofunction:: renderdoc.GetLogFile +.. autofunction:: renderdoc.GetVersionString +.. autofunction:: renderdoc.GetCommitHash + +Maths & Utilities +----------------- + .. autofunction:: renderdoc.Maths_FloatToHalf .. autofunction:: renderdoc.Maths_HalfToFloat .. autofunction:: renderdoc.Topology_NumVerticesPerPrimitive .. autofunction:: renderdoc.Topology_VertexOffset - -.. autofunction:: renderdoc.SupportLocalReplay -.. autofunction:: renderdoc.CreateReplayRenderer -.. autofunction:: renderdoc.CreateTargetControl -.. autofunction:: renderdoc.EnumerateRemoteTargets -.. autofunction:: renderdoc.GetDefaultRemoteServerPort -.. autofunction:: renderdoc.CreateRemoteServerConnection -.. autofunction:: renderdoc.BecomeRemoteServer -.. autofunction:: renderdoc.GetDefaultCaptureOptions -.. autofunction:: renderdoc.StartGlobalHook -.. autofunction:: renderdoc.ExecuteAndInject -.. autofunction:: renderdoc.InjectIntoProcess -.. autofunction:: renderdoc.StartSelfHostCapture -.. autofunction:: renderdoc.EndSelfHostCapture -.. autofunction:: renderdoc.SetDebugLogFile -.. autofunction:: renderdoc.GetLogFile -.. autofunction:: renderdoc.GetThumbnail -.. autofunction:: renderdoc.GetVersionString -.. autofunction:: renderdoc.GetCommitHash - -.. autofunction:: renderdoc.IsD3D .. autofunction:: renderdoc.PatchList_Count .. autofunction:: renderdoc.PatchList_Topology +.. autofunction:: renderdoc.IsD3D .. autofunction:: renderdoc.MaskForStage +.. autofunction:: renderdoc.StartSelfHostCapture +.. autofunction:: renderdoc.EndSelfHostCapture diff --git a/docs/python_api/main_ifaces.rst b/docs/python_api/main_ifaces.rst index 1b0384d3b..81c6dedba 100644 --- a/docs/python_api/main_ifaces.rst +++ b/docs/python_api/main_ifaces.rst @@ -1,20 +1,41 @@ Primary Interfaces ================== +.. contents:: + .. module:: renderdoc +ReplayRenderer +-------------- + .. autoclass:: renderdoc.ReplayRenderer :members: :undoc-members: +ReplayOutput +------------ + .. autoclass:: renderdoc.ReplayOutput :members: :undoc-members: +TargetControl +------------- + .. autoclass:: renderdoc.TargetControl :members: :undoc-members: +RemoteServer +------------ + .. autoclass:: renderdoc.RemoteServer :members: :undoc-members: + +CaptureFile +----------- + +.. autoclass:: renderdoc.CaptureFile + :members: + :undoc-members: diff --git a/qrenderdoc/Code/RenderManager.cpp b/qrenderdoc/Code/RenderManager.cpp index 9cce10ca1..e21ae9517 100644 --- a/qrenderdoc/Code/RenderManager.cpp +++ b/qrenderdoc/Code/RenderManager.cpp @@ -396,10 +396,21 @@ void RenderManager::run() IReplayRenderer *renderer = NULL; if(m_Remote) + { std::tie(m_CreateStatus, renderer) = m_Remote->OpenCapture(~0U, m_Logfile.toUtf8().data(), m_Progress); + } else - m_CreateStatus = RENDERDOC_CreateReplayRenderer(m_Logfile.toUtf8().data(), m_Progress, &renderer); + { + ICaptureFile *file = RENDERDOC_OpenCaptureFile(m_Logfile.toUtf8().data()); + + m_CreateStatus = file->OpenStatus(); + + if(m_CreateStatus == ReplayStatus::Succeeded) + std::tie(m_CreateStatus, renderer) = file->OpenCapture(m_Progress); + + file->Shutdown(); + } if(renderer == NULL) return; diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 025f8910e..e56fde9f4 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -411,8 +411,8 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local if(m_Ctx.LogLoading()) return; - rdctype::str driver; - rdctype::str machineIdent; + QString driver; + QString machineIdent; ReplaySupport support = ReplaySupport::Unsupported; bool remoteReplay = @@ -420,7 +420,22 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local if(local) { - support = RENDERDOC_SupportLocalReplay(filename.toUtf8().data(), &driver, &machineIdent); + ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename.toUtf8().data()); + + if(file->OpenStatus() != ReplayStatus::Succeeded) + { + RDDialog::critical(NULL, tr("Error opening capture"), + tr("Couldn't open file '%1'").arg(filename)); + + file->Shutdown(); + return; + } + + driver = QString::fromUtf8(file->DriverName()); + machineIdent = QString::fromUtf8(file->RecordedMachineIdent()); + support = file->LocalReplaySupport(); + + file->Shutdown(); // if the return value suggests remote replay, and it's not already selected, AND the user // hasn't previously chosen to always replay locally without being prompted, ask if they'd @@ -428,7 +443,7 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local if(support == ReplaySupport::SuggestRemote && !remoteReplay && !m_Ctx.Config().AlwaysReplayLocally) { - SuggestRemoteDialog dialog(ToQStr(driver), ToQStr(machineIdent), this); + SuggestRemoteDialog dialog(driver, machineIdent, this); FillRemotesMenu(dialog.remotesMenu(), false); @@ -499,13 +514,13 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local // if driver is empty something went wrong loading the log, let it be handled as usual // below. Otherwise indicate that support is missing. - if(driver.count > 0 && support == ReplaySupport::Unsupported) + if(!driver.isEmpty() && support == ReplaySupport::Unsupported) { if(remoteReplay) { QString remoteMessage = tr("This log was captured with %1 and cannot be replayed on %2.\n\n") - .arg(driver.c_str()) + .arg(driver) .arg(m_Ctx.Renderer().CurrentRemote()->Hostname); remoteMessage += "Try selecting a different remote context in the status bar."; @@ -515,7 +530,7 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local else { QString remoteMessage = - tr("This log was captured with %1 and cannot be replayed locally.\n\n").arg(driver.c_str()); + tr("This log was captured with %1 and cannot be replayed locally.\n\n").arg(driver); remoteMessage += "Try selecting a remote context in the status bar."; diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index c8d09b940..7c96fd207 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -113,6 +113,7 @@ set(sources os/os_specific.h replay/app_api.cpp replay/capture_options.cpp + replay/capture_file.cpp replay/entry_points.cpp replay/replay_driver.h replay/replay_output.cpp diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index cb047d166..111170e5a 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1019,6 +1019,84 @@ protected: ~IRemoteServer() = default; }; +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 +{ + DOCUMENT("Closes the handle."); + virtual void Shutdown() = 0; + + DOCUMENT(R"(Retrieves the status of the handle. + +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. + +:return: The status of the handle to the file. +:rtype: ReplayStatus +)"); + virtual ReplayStatus OpenStatus() = 0; + + DOCUMENT(R"(Retrieves the filename used to open this handle. + +This filename is exactly as specified without any modificaton to make it an absolute path. + +:return: The filename used to create this handle. +:rtype: ``str`` +)"); + virtual const char *Filename() = 0; + + DOCUMENT(R"(Queries for how well a particular capture is supported on the local machine. + +:return: How much support for replay exists locally. +:rtype: ReplaySupport +)"); + virtual ReplaySupport LocalReplaySupport() = 0; + + DOCUMENT(R"(Retrieves the name of the driver that was used to create this capture. + +:return: A simple string identifying the driver used to make the capture. +:rtype: ``str`` +)"); + virtual const char *DriverName() = 0; + + DOCUMENT(R"(Retrieves the identifying string describing what type of machine created this capture. + +:return: A string identifying the machine ident used to make the capture. +:rtype: ``str`` +)"); + virtual const char *RecordedMachineIdent() = 0; + + DOCUMENT(R"(Opens a capture for replay locally and returns a handle to the capture. + +This function will block until the capture is fully loaded and ready. + +Once the replay is created, this :class:`CaptureFile` can be shut down, there is no dependency on it +by the :class:`ReplayRenderer`. + +:param float progress: A reference to a ``float`` value that will be updated as the copy happens + from ``0.0`` to ``1.0``. The parameter can be ``None`` if no progress update is desired. +:return: A tuple containing the status of opening the capture, whether success or failure, and the + resulting :class:`ReplayRenderer` handle if successful. +:rtype: ``tuple`` of :class:`ReplayStatus` and :class:`ReplayRenderer`. +)"); + virtual rdctype::pair OpenCapture(float *progress) = 0; + + DOCUMENT(R"(Retrieves the embedded thumbnail from the capture. + +: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 + resolution. +:rtype: ``bytes``. + )"); + virtual rdctype::array GetThumbnail(FileType type, uint32_t maxsize) = 0; + +protected: + ICaptureFile() = default; + ~ICaptureFile() = default; +}; + ////////////////////////////////////////////////////////////////////////// // camera ////////////////////////////////////////////////////////////////////////// @@ -1091,39 +1169,21 @@ extern "C" RENDERDOC_API uint32_t RENDERDOC_CC Topology_VertexOffset(Topology to uint32_t primitive); ////////////////////////////////////////////////////////////////////////// -// Create a replay renderer, for playback and analysis. +// Create a capture handle. // -// Takes the filename of the log. Returns NULL in the case of any error. +// 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`. ////////////////////////////////////////////////////////////////////////// -DOCUMENT(R"(Queries for how well a particular capture is supported on the local machine, and returns -some simple properties about the capture. +DOCUMENT(R"(Create a capture handle. -:param str path: The path to the capture. -:param str driverName: A reference to a ``str`` that is filled with the name of the driver used in - the capture. -:param str driverName: A reference to a ``str`` that is filled with the identifier of the type of - machine used such as OS and architecture. -:return: How much support for replay exists locally. +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`. + +:return: A handle to the specified path. :rtype: ReplaySupport )"); -extern "C" RENDERDOC_API ReplaySupport RENDERDOC_CC RENDERDOC_SupportLocalReplay( - const char *logfile, rdctype::str *driverName, rdctype::str *recordMachineIdent); - -DOCUMENT(R"(Opens a capture for replay locally and returns a handle to the capture. - -This function will block until the capture is fully loaded and ready. - -:param str logfile: The path to the capture. -:param float progress: A reference to a ``float`` value that will be updated as the copy happens - from ``0.0`` to ``1.0``. -:param ReplayRenderer rend: A reference to a :class:`ReplayRenderer` where the capture handle will - be stored. -:return: The status of opening the capture, whether success or failure. -:rtype: ReplayStatus -)"); -extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC -RENDERDOC_CreateReplayRenderer(const char *logfile, float *progress, IReplayRenderer **rend); +extern "C" RENDERDOC_API ICaptureFile *RENDERDOC_CC RENDERDOC_OpenCaptureFile(const char *logfile); ////////////////////////////////////////////////////////////////////////// // Target Control @@ -1315,17 +1375,6 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_LogMessage(LogType type, co const char *file, unsigned int line, const char *text); -DOCUMENT(R"(Retrieves the embedded thumbnail from a capture. - -:param str filename: The filename of the capture to open. -: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. -:param bytes buf: A reference to a ``bytes`` to receive the data, empty if something went wrong. -)"); -extern "C" RENDERDOC_API bool32 RENDERDOC_CC RENDERDOC_GetThumbnail(const char *filename, - FileType type, uint32_t maxsize, - rdctype::array *buf); - DOCUMENT(R"(Retrieves the version string. This will be in the form "MAJOR.MINOR" diff --git a/renderdoc/core/target_control.cpp b/renderdoc/core/target_control.cpp index 1dc851726..972b97f03 100644 --- a/renderdoc/core/target_control.cpp +++ b/renderdoc/core/target_control.cpp @@ -128,7 +128,13 @@ void RenderDoc::TargetControlClientThread(void *s) ser.Serialise("", path); rdctype::array buf; - RENDERDOC_GetThumbnail(captures.back().path.c_str(), FileType::JPG, 0, &buf); + + ICaptureFile *file = RENDERDOC_OpenCaptureFile(captures.back().path.c_str()); + if(file->OpenStatus() == ReplayStatus::Succeeded) + { + buf = file->GetThumbnail(FileType::JPG, 0); + } + file->Shutdown(); size_t sz = buf.size(); ser.Serialise("", buf.count); diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index f995ae9b6..709a3e97c 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -385,6 +385,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 6b69bdc9a..853fde7a9 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -452,6 +452,9 @@ 3rdparty\tinyfiledialogs + + Replay + diff --git a/renderdoc/replay/capture_file.cpp b/renderdoc/replay/capture_file.cpp new file mode 100644 index 000000000..77cb03b26 --- /dev/null +++ b/renderdoc/replay/capture_file.cpp @@ -0,0 +1,249 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2017 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "api/replay/renderdoc_replay.h" +#include "core/core.h" +#include "jpeg-compressor/jpgd.h" +#include "jpeg-compressor/jpge.h" +#include "replay/replay_renderer.h" +#include "serialise/serialiser.h" +#include "stb/stb_image_resize.h" +#include "stb/stb_image_write.h" + +static void writeToByteVector(void *context, void *data, int size) +{ + std::vector *vec = (std::vector *)context; + byte *start = (byte *)data; + byte *end = start + size; + vec->insert(vec->end(), start, end); +} + +class CaptureFile : public ICaptureFile +{ +public: + CaptureFile(const char *f); + virtual ~CaptureFile() {} + 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(); } + rdctype::pair OpenCapture(float *progress); + + rdctype::array GetThumbnail(FileType type, uint32_t maxsize); + +private: + std::string m_Filename, m_DriverName, m_Ident; + RDCDriver m_DriverType; + ReplayStatus m_Status; + ReplaySupport m_Support; +}; + +CaptureFile::CaptureFile(const char *f) +{ + m_Filename = f; + + m_DriverType = RDC_Unknown; + uint64_t fileMachineIdent = 0; + m_Status = RenderDoc::Inst().FillInitParams(Filename(), m_DriverType, m_DriverName, + fileMachineIdent, NULL); + + if(m_Status != ReplayStatus::Succeeded) + { + m_Support = ReplaySupport::Unsupported; + } + else + { + m_Support = RenderDoc::Inst().HasReplayDriver(m_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; + } + } +} + +rdctype::pair CaptureFile::OpenCapture(float *progress) +{ + if(m_Status != ReplayStatus::Succeeded) + return rdctype::make_pair(m_Status, NULL); + + ReplayRenderer *render = new ReplayRenderer(); + ReplayStatus ret; + + RenderDoc::Inst().SetProgressPtr(progress); + + ret = render->CreateDevice(Filename()); + + RenderDoc::Inst().SetProgressPtr(NULL); + + if(ret != ReplayStatus::Succeeded) + SAFE_DELETE(render); + + return rdctype::make_pair(ret, render); +} + +rdctype::array CaptureFile::GetThumbnail(FileType type, uint32_t maxsize) +{ + rdctype::array buf; + + Serialiser ser(Filename(), Serialiser::READING, false); + + if(ser.HasError()) + return buf; + + ser.Rewind(); + + 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); + } + + if(jpgbuf == NULL) + return buf; + + // if the desired output is jpg and either there's no max size or it's already satisfied, + // return the data directly + if(type == FileType::JPG && (maxsize == 0 || (maxsize > thumbwidth && maxsize > thumbheight))) + { + create_array_init(buf, thumblen, jpgbuf); + } + else + { + // otherwise we need to decode, resample maybe, and re-encode + + int w = (int)thumbwidth; + int h = (int)thumbheight; + int comp = 3; + byte *thumbpixels = + jpgd::decompress_jpeg_image_from_memory(jpgbuf, (int)thumblen, &w, &h, &comp, 3); + + if(maxsize != 0) + { + uint32_t clampedWidth = RDCMIN(maxsize, thumbwidth); + uint32_t clampedHeight = RDCMIN(maxsize, thumbheight); + + if(clampedWidth != thumbwidth || clampedHeight != thumbheight) + { + // preserve aspect ratio, take the smallest scale factor and multiply both + float scaleX = float(clampedWidth) / float(thumbwidth); + float scaleY = float(clampedHeight) / float(thumbheight); + + if(scaleX < scaleY) + clampedHeight = uint32_t(scaleX * thumbheight); + else if(scaleY < scaleX) + clampedWidth = uint32_t(scaleY * thumbwidth); + + byte *resizedpixels = (byte *)malloc(3 * clampedWidth * clampedHeight); + + stbir_resize_uint8_srgb(thumbpixels, thumbwidth, thumbheight, 0, resizedpixels, + clampedWidth, clampedHeight, 0, 3, -1, 0); + + free(thumbpixels); + + thumbpixels = resizedpixels; + thumbwidth = clampedWidth; + thumbheight = clampedHeight; + } + } + + std::vector encodedBytes; + + switch(type) + { + case FileType::JPG: + { + int len = thumbwidth * thumbheight * 3; + encodedBytes.resize(len); + jpge::params p; + p.m_quality = 90; + jpge::compress_image_to_jpeg_file_in_memory(&encodedBytes[0], len, (int)thumbwidth, + (int)thumbheight, 3, thumbpixels, p); + encodedBytes.resize(len); + break; + } + case FileType::PNG: + { + stbi_write_png_to_func(&writeToByteVector, &encodedBytes, (int)thumbwidth, (int)thumbheight, + 3, thumbpixels, 0); + break; + } + case FileType::TGA: + { + stbi_write_tga_to_func(&writeToByteVector, &encodedBytes, (int)thumbwidth, (int)thumbheight, + 3, thumbpixels); + break; + } + case FileType::BMP: + { + stbi_write_bmp_to_func(&writeToByteVector, &encodedBytes, (int)thumbwidth, (int)thumbheight, + 3, thumbpixels); + break; + } + default: + { + RDCERR("Unsupported file type %d in thumbnail fetch", type); + free(thumbpixels); + delete[] jpgbuf; + return buf; + } + } + + buf = encodedBytes; + + free(thumbpixels); + } + + delete[] jpgbuf; + + return buf; +} + +extern "C" RENDERDOC_API ICaptureFile *RENDERDOC_CC RENDERDOC_OpenCaptureFile(const char *logfile) +{ + return new CaptureFile(logfile); +} \ No newline at end of file diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index 09633516a..e599f1aa7 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -28,15 +28,10 @@ #include "api/replay/version.h" #include "common/common.h" #include "core/core.h" -#include "jpeg-compressor/jpgd.h" -#include "jpeg-compressor/jpge.h" #include "maths/camera.h" #include "maths/formatpacking.h" -#include "replay/replay_renderer.h" -#include "serialise/serialiser.h" +#include "replay/type_helpers.h" #include "serialise/string_utils.h" -#include "stb/stb_image_resize.h" -#include "stb/stb_image_write.h" // these entry points are for the replay/analysis side - not for the application. @@ -339,66 +334,39 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_TriggerExceptionHandler(voi extern "C" RENDERDOC_API ReplaySupport RENDERDOC_CC RENDERDOC_SupportLocalReplay( const char *logfile, rdctype::str *driver, rdctype::str *recordMachineIdent) { - if(logfile == NULL) - return ReplaySupport::Unsupported; - - RDCDriver driverType = RDC_Unknown; - string driverName = ""; - uint64_t fileMachineIdent = 0; - RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, fileMachineIdent, NULL); + ICaptureFile *file = RENDERDOC_OpenCaptureFile(logfile); if(driver) - *driver = driverName; + *driver = file->DriverName(); - bool supported = RenderDoc::Inst().HasReplayDriver(driverType); + if(recordMachineIdent) + *recordMachineIdent = file->RecordedMachineIdent(); - if(!supported) - return ReplaySupport::Unsupported; + ReplaySupport support = file->LocalReplaySupport(); - if(fileMachineIdent != 0) - { - uint64_t machineIdent = OSUtility::GetMachineIdent(); + file->Shutdown(); - if(recordMachineIdent) - *recordMachineIdent = OSUtility::MakeMachineIdentString(fileMachineIdent); - - if((machineIdent & OSUtility::MachineIdent_OS_Mask) != - (fileMachineIdent & OSUtility::MachineIdent_OS_Mask)) - return ReplaySupport::SuggestRemote; - } - - return ReplaySupport::Supported; + return support; } extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC RENDERDOC_CreateReplayRenderer(const char *logfile, float *progress, IReplayRenderer **rend) { - if(rend == NULL) - return ReplayStatus::InternalError; + ICaptureFile *file = RENDERDOC_OpenCaptureFile(logfile); - RenderDoc::Inst().SetProgressPtr(progress); - - ReplayRenderer *render = new ReplayRenderer(); - - if(!render) - { - RenderDoc::Inst().SetProgressPtr(NULL); - return ReplayStatus::InternalError; - } - - ReplayStatus ret = render->CreateDevice(logfile); + ReplayStatus ret = file->OpenStatus(); if(ret != ReplayStatus::Succeeded) { - delete render; - RenderDoc::Inst().SetProgressPtr(NULL); + file->Shutdown(); return ret; } - *rend = render; + std::tie(ret, *rend) = file->OpenCapture(progress); - RenderDoc::Inst().SetProgressPtr(NULL); - return ReplayStatus::Succeeded; + file->Shutdown(); + + return ret; } extern "C" RENDERDOC_API uint32_t RENDERDOC_CC @@ -429,142 +397,20 @@ RENDERDOC_InjectIntoProcess(uint32_t pid, const rdctype::array *vec = (std::vector *)context; - byte *start = (byte *)data; - byte *end = start + size; - vec->insert(vec->end(), start, end); -} - extern "C" RENDERDOC_API bool32 RENDERDOC_CC RENDERDOC_GetThumbnail(const char *filename, FileType type, uint32_t maxsize, rdctype::array *buf) { - Serialiser ser(filename, Serialiser::READING, false); + ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename); - if(ser.HasError()) - return false; - - ser.Rewind(); - - int chunkType = ser.PushContext(NULL, NULL, 1, false); - - if(chunkType != THUMBNAIL_DATA) - return false; - - bool HasThumbnail = false; - ser.Serialise(NULL, HasThumbnail); - - if(!HasThumbnail) - return false; - - byte *jpgbuf = NULL; - size_t thumblen = 0; - uint32_t thumbwidth = 0, thumbheight = 0; + if(file->OpenStatus() != ReplayStatus::Succeeded) { - ser.Serialise("ThumbWidth", thumbwidth); - ser.Serialise("ThumbHeight", thumbheight); - ser.SerialiseBuffer("ThumbnailPixels", jpgbuf, thumblen); + file->Shutdown(); + return false; } - if(jpgbuf == NULL) - return false; - - // if the desired output is jpg and either there's no max size or it's already satisfied, - // return the data directly - if(type == FileType::JPG && (maxsize == 0 || (maxsize > thumbwidth && maxsize > thumbheight))) - { - create_array_init(*buf, thumblen, jpgbuf); - } - else - { - // otherwise we need to decode, resample maybe, and re-encode - - int w = (int)thumbwidth; - int h = (int)thumbheight; - int comp = 3; - byte *thumbpixels = - jpgd::decompress_jpeg_image_from_memory(jpgbuf, (int)thumblen, &w, &h, &comp, 3); - - if(maxsize != 0) - { - uint32_t clampedWidth = RDCMIN(maxsize, thumbwidth); - uint32_t clampedHeight = RDCMIN(maxsize, thumbheight); - - if(clampedWidth != thumbwidth || clampedHeight != thumbheight) - { - // preserve aspect ratio, take the smallest scale factor and multiply both - float scaleX = float(clampedWidth) / float(thumbwidth); - float scaleY = float(clampedHeight) / float(thumbheight); - - if(scaleX < scaleY) - clampedHeight = uint32_t(scaleX * thumbheight); - else if(scaleY < scaleX) - clampedWidth = uint32_t(scaleY * thumbwidth); - - byte *resizedpixels = (byte *)malloc(3 * clampedWidth * clampedHeight); - - stbir_resize_uint8_srgb(thumbpixels, thumbwidth, thumbheight, 0, resizedpixels, - clampedWidth, clampedHeight, 0, 3, -1, 0); - - free(thumbpixels); - - thumbpixels = resizedpixels; - thumbwidth = clampedWidth; - thumbheight = clampedHeight; - } - } - - std::vector encodedBytes; - - switch(type) - { - case FileType::JPG: - { - int len = thumbwidth * thumbheight * 3; - encodedBytes.resize(len); - jpge::params p; - p.m_quality = 90; - jpge::compress_image_to_jpeg_file_in_memory(&encodedBytes[0], len, (int)thumbwidth, - (int)thumbheight, 3, thumbpixels, p); - encodedBytes.resize(len); - break; - } - case FileType::PNG: - { - stbi_write_png_to_func(&writeToByteVector, &encodedBytes, (int)thumbwidth, (int)thumbheight, - 3, thumbpixels, 0); - break; - } - case FileType::TGA: - { - stbi_write_tga_to_func(&writeToByteVector, &encodedBytes, (int)thumbwidth, (int)thumbheight, - 3, thumbpixels); - break; - } - case FileType::BMP: - { - stbi_write_bmp_to_func(&writeToByteVector, &encodedBytes, (int)thumbwidth, (int)thumbheight, - 3, thumbpixels); - break; - } - default: - { - RDCERR("Unsupported file type %d in thumbnail fetch", type); - free(thumbpixels); - delete[] jpgbuf; - return false; - } - } - - *buf = encodedBytes; - - free(thumbpixels); - } - - delete[] jpgbuf; - + *buf = file->GetThumbnail(type, maxsize); + file->Shutdown(); return true; } diff --git a/renderdoccmd/renderdoccmd.cpp b/renderdoccmd/renderdoccmd.cpp index 367440784..9fde8080d 100644 --- a/renderdoccmd/renderdoccmd.cpp +++ b/renderdoccmd/renderdoccmd.cpp @@ -252,10 +252,19 @@ struct ThumbCommand : public Command } rdctype::array buf; - bool32 ret = - RENDERDOC_GetThumbnail(filename.c_str(), type, parser.get("max-size"), &buf); - if(!ret) + ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename.c_str()); + if(file->OpenStatus() == ReplayStatus::Succeeded) + { + buf = file->GetThumbnail(FileType::JPG, 0); + } + else + { + std::cerr << "Couldn't open '" << filename << "'" << std::endl; + } + file->Shutdown(); + + if(buf.empty()) { std::cerr << "Couldn't fetch the thumbnail in '" << filename << "'" << std::endl; } @@ -522,9 +531,19 @@ struct ReplayCommand : public Command { std::cout << "Replaying '" << filename << "' locally.." << std::endl; - float progress = 0.0f; + ICaptureFile *file = RENDERDOC_OpenCaptureFile(filename.c_str()); + + if(file->OpenStatus() != ReplayStatus::Succeeded) + { + std::cerr << "Couldn't load '" << filename << "'." << std::endl; + return 1; + } + IReplayRenderer *renderer = NULL; - ReplayStatus status = RENDERDOC_CreateReplayRenderer(filename.c_str(), &progress, &renderer); + ReplayStatus status = ReplayStatus::InternalError; + std::tie(status, renderer) = file->OpenCapture(NULL); + + file->Shutdown(); if(status == ReplayStatus::Succeeded) { @@ -536,6 +555,7 @@ struct ReplayCommand : public Command else { std::cerr << "Couldn't load and replay '" << filename << "'." << std::endl; + return 1; } } return 0;