From 13bf08a99e9e906215fc6fed0bbeae398a86f19e Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 5 Jul 2017 16:27:38 +0100 Subject: [PATCH] Remove disassembly from shader reflection, request it on demand --- .../VulkanPipelineStateViewer.cpp | 29 ++- qrenderdoc/Windows/ShaderViewer.cpp | 81 +++++++- qrenderdoc/Windows/ShaderViewer.h | 4 + renderdoc/api/replay/renderdoc_replay.h | 20 ++ renderdoc/api/replay/shader_types.h | 3 - renderdoc/core/image_viewer.cpp | 2 + renderdoc/core/replay_proxy.cpp | 61 +++++- renderdoc/core/replay_proxy.h | 6 + renderdoc/driver/d3d11/d3d11_common.cpp | 2 - renderdoc/driver/d3d11/d3d11_replay.cpp | 21 ++ renderdoc/driver/d3d11/d3d11_replay.h | 3 + renderdoc/driver/d3d12/d3d12_common.cpp | 2 - renderdoc/driver/d3d12/d3d12_replay.cpp | 21 ++ renderdoc/driver/d3d12/d3d12_replay.h | 3 + renderdoc/driver/gl/gl_driver.h | 1 + renderdoc/driver/gl/gl_replay.cpp | 26 +++ renderdoc/driver/gl/gl_replay.h | 3 + renderdoc/driver/gl/gl_shader_refl.cpp | 2 - .../driver/gl/wrappers/gl_shader_funcs.cpp | 2 - renderdoc/driver/vulkan/vk_info.h | 1 + renderdoc/driver/vulkan/vk_replay.cpp | 34 +++- renderdoc/driver/vulkan/vk_replay.h | 3 + renderdoc/replay/replay_controller.cpp | 30 +++ renderdoc/replay/replay_controller.h | 3 + renderdoc/replay/replay_driver.h | 3 + renderdocui/Interop/ReplayRenderer.cs | 34 ++++ renderdocui/Interop/Shader.cs | 5 +- .../VulkanPipelineStateViewer.cs | 11 +- renderdocui/Windows/ShaderViewer.cs | 183 +++++++++++++----- 29 files changed, 509 insertions(+), 90 deletions(-) diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index bdcb04e9a..35516ade3 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -2305,23 +2305,38 @@ void VulkanPipelineStateViewer::shaderEdit_clicked() bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files, mainfile); - if(!hasOrigSource) + if(hasOrigSource) + { + if(files.empty()) + return; + } + else { QString glsl; if(!m_Ctx.Config().SPIRVDisassemblers.isEmpty()) glsl = disassembleSPIRV(shaderDetails); - if(glsl.isEmpty()) - glsl = ToQStr(shaderDetails->Disassembly); - mainfile = lit("generated.glsl"); files[mainfile] = glsl; - } - if(files.empty()) - return; + if(glsl.isEmpty()) + { + m_Ctx.Replay().AsyncInvoke( + [this, stage, shaderDetails, entryFunc, mainfile](IReplayController *r) { + rdctype::str disasm = r->DisassembleShader(shaderDetails, ""); + + GUIInvoke::call([this, stage, shaderDetails, entryFunc, mainfile, disasm]() { + QStringMap fileMap; + fileMap[mainfile] = ToQStr(disasm); + m_Common.EditShader(stage->stage, stage->Object, shaderDetails, entryFunc, fileMap, + mainfile); + }); + }); + return; + } + } m_Common.EditShader(stage->stage, stage->Object, shaderDetails, entryFunc, files, mainfile); } diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 052ba0c59..22969f17e 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include "ShaderViewer.h" +#include #include #include #include @@ -98,7 +99,6 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) MakeEditor(lit("scintillaDisassem"), QString(), m_Ctx.APIProps().pipelineType == GraphicsAPI::Vulkan ? SCLEX_GLSL : SCLEX_HLSL); m_DisassemblyView->setReadOnly(true); - m_DisassemblyView->setWindowTitle(tr("Disassembly")); QObject::connect(m_DisassemblyView, &ScintillaEdit::keyPressed, this, &ShaderViewer::readonly_keyPressed); @@ -123,8 +123,33 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) m_Scintillas.push_back(m_DisassemblyView); - ui->docking->addToolWindow(m_DisassemblyView, ToolWindowManager::EmptySpace); - ui->docking->setToolWindowProperties(m_DisassemblyView, + m_DisassemblyFrame = new QWidget(this); + m_DisassemblyFrame->setWindowTitle(tr("Disassembly")); + + QFrame *disasmToolbar = new QFrame(this); + disasmToolbar->setFrameShape(QFrame::Panel); + disasmToolbar->setFrameShadow(QFrame::Raised); + + QHBoxLayout *toolbarlayout = new QHBoxLayout(disasmToolbar); + toolbarlayout->setSpacing(2); + toolbarlayout->setContentsMargins(2, 2, 2, 2); + + m_DisassemblyType = new QComboBox(disasmToolbar); + m_DisassemblyType->setMaxVisibleItems(12); + m_DisassemblyType->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + toolbarlayout->addWidget(new QLabel(tr("Disassembly type:"), disasmToolbar)); + toolbarlayout->addWidget(m_DisassemblyType); + toolbarlayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + QVBoxLayout *framelayout = new QVBoxLayout(m_DisassemblyFrame); + framelayout->setSpacing(0); + framelayout->setMargin(0); + framelayout->addWidget(disasmToolbar); + framelayout->addWidget(m_DisassemblyView); + + ui->docking->addToolWindow(m_DisassemblyFrame, ToolWindowManager::EmptySpace); + ui->docking->setToolWindowProperties(m_DisassemblyFrame, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow | ToolWindowManager::AlwaysDisplayFullTabs); @@ -175,7 +200,7 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) void ShaderViewer::editShader(bool customShader, const QString &entryPoint, const QStringMap &files) { m_Scintillas.removeOne(m_DisassemblyView); - ui->docking->removeToolWindow(m_DisassemblyView); + ui->docking->removeToolWindow(m_DisassemblyFrame); // hide watch, constants, variables ui->watch->hide(); @@ -280,10 +305,27 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR if(shader) { - // read-only applies to us too! - m_DisassemblyView->setReadOnly(false); - m_DisassemblyView->setText(shader->Disassembly.c_str()); - m_DisassemblyView->setReadOnly(true); + m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { + rdctype::array targets = r->GetDisassemblyTargets(); + + rdctype::str disasm = r->DisassembleShader(m_ShaderDetails, ""); + + GUIInvoke::call([this, targets, disasm]() { + QStringList targetNames; + for(const rdctype::str &t : targets) + targetNames << ToQStr(t); + + m_DisassemblyType->addItems(targetNames); + m_DisassemblyType->setCurrentIndex(0); + QObject::connect(m_DisassemblyType, OverloadedSlot::of(&QComboBox::currentIndexChanged), + this, &ShaderViewer::disassemble_typeChanged); + + // read-only applies to us too! + m_DisassemblyView->setReadOnly(false); + m_DisassemblyView->setText(disasm.c_str()); + m_DisassemblyView->setReadOnly(true); + }); + }); } // we always want to highlight words/registers @@ -360,7 +402,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->watch->setWindowTitle(tr("Watch")); ui->docking->addToolWindow( ui->watch, ToolWindowManager::AreaReference(ToolWindowManager::BottomOf, - ui->docking->areaOf(m_DisassemblyView), 0.25f)); + ui->docking->areaOf(m_DisassemblyFrame), 0.25f)); ui->docking->setToolWindowProperties( ui->watch, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); @@ -506,7 +548,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->inputSig->setWindowTitle(tr("Input Signature")); ui->docking->addToolWindow(ui->inputSig, ToolWindowManager::AreaReference( ToolWindowManager::BottomOf, - ui->docking->areaOf(m_DisassemblyView), 0.2f)); + ui->docking->areaOf(m_DisassemblyFrame), 0.2f)); ui->docking->setToolWindowProperties( ui->inputSig, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); @@ -770,6 +812,25 @@ void ShaderViewer::disassembly_buttonReleased(QMouseEvent *event) } } +void ShaderViewer::disassemble_typeChanged(int index) +{ + if(m_ShaderDetails == NULL) + return; + + QByteArray target = m_DisassemblyType->currentText().toUtf8(); + + m_Ctx.Replay().AsyncInvoke([this, target](IReplayController *r) { + rdctype::str disasm = r->DisassembleShader(m_ShaderDetails, target.data()); + + GUIInvoke::call([this, disasm]() { + m_DisassemblyView->setReadOnly(false); + m_DisassemblyView->setText(disasm.c_str()); + m_DisassemblyView->setReadOnly(true); + m_DisassemblyView->emptyUndoBuffer(); + }); + }); +} + void ShaderViewer::watch_keyPress(QKeyEvent *event) { if(event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index 3d818ba31..22c395887 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -40,6 +40,7 @@ class FindReplace; class QTableWidgetItem; class QKeyEvent; class QMouseEvent; +class QComboBox; // from Scintilla typedef intptr_t sptr_t; @@ -117,6 +118,7 @@ private slots: void editable_keyPressed(QKeyEvent *event); void disassembly_contextMenu(const QPoint &pos); void disassembly_buttonReleased(QMouseEvent *event); + void disassemble_typeChanged(int index); void watch_keyPress(QKeyEvent *event); void performFind(); void performFindAll(); @@ -169,6 +171,8 @@ private: const ShaderReflection *m_ShaderDetails = NULL; ShaderStage m_Stage; ScintillaEdit *m_DisassemblyView = NULL; + QWidget *m_DisassemblyFrame = NULL; + QComboBox *m_DisassemblyType = NULL; ScintillaEdit *m_Errors = NULL; ScintillaEdit *m_FindResults = NULL; QList m_Scintillas; diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index c2e047db1..f20aa7522 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -515,6 +515,26 @@ You should use :meth:`GetAPIProperties` to determine the API of the capture. )"); virtual VKPipe::State GetVulkanPipelineState() = 0; + DOCUMENT(R"(Retrieve the list of possible disassembly targets for :meth:`DisassembleShader`. The +values are implementation dependent but will always include a default target first which is the +native disassembly of the shader. Further options may be available for additional diassembly views +or hardware-specific ISA formats. + +:return: The list of disassembly targets available. +:rtype: ``list`` of ``str`` +)"); + virtual rdctype::array GetDisassemblyTargets() = 0; + + DOCUMENT(R"(Retrieve the disassembly for a given shader, for the given disassembly target. + +:param ShaderReflection refl: The shader reflection details of the shader to disassemble +:param str target: The name of the disassembly target to generate for. Must be one of the values + returned by :meth:`GetDisassemblyTargets`, or empty to use the default generation. +:return: The disassembly text, or an error message if something went wrong. +:rtype: str +)"); + virtual rdctype::str DisassembleShader(const ShaderReflection *refl, const char *target) = 0; + DOCUMENT(R"(Builds a shader suitable for running on the local replay instance as a custom shader. The language used is native to the local renderer - HLSL for D3D based renderers, GLSL otherwise. diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 7cc9be925..3c2a80224 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -480,9 +480,6 @@ struct ShaderReflection "A :class:`ShaderDebugChunk` containing any embedded debugging information in this shader."); ShaderDebugChunk DebugInfo; - DOCUMENT("The disassembly for this shader, in an API specific representation."); - rdctype::str Disassembly; - DOCUMENT("A raw ``bytes`` dump of the original shader, encoded in API specific binary form."); rdctype::array RawBytes; diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 42bd1b07b..a5927bf86 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -198,6 +198,8 @@ public: return ResourceId(); } ShaderReflection *GetShader(ResourceId shader, string entryPoint) { return NULL; } + vector GetDisassemblyTargets() { return {"N/A"}; } + string DisassembleShader(const ShaderReflection *refl, const string &target) { return ""; } bool HasCallstacks() { return false; } void InitCallstackResolver() {} Callstack::StackResolver *GetCallstackResolver() { return NULL; } diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 6a45b904b..8d68dbde2 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -220,8 +220,6 @@ void Serialiser::Serialise(const char *name, ShaderReflection &el) SerialisePODArray<3>("", el.DispatchThreadsDimension); - Serialise("", el.Disassembly); - Serialise("", el.RawBytes); Serialise("", el.InputSig); @@ -234,7 +232,7 @@ void Serialiser::Serialise(const char *name, ShaderReflection &el) Serialise("", el.Interfaces); - SIZE_CHECK(192); + SIZE_CHECK(176); } template <> @@ -2223,6 +2221,8 @@ bool ReplayProxy::Tick(int type, Serialiser *incomingPacket) DebugThread(0, dummy1, dummy2); break; } + case eReplayProxy_DisassembleShader: DisassembleShader(NULL, ""); break; + case eReplayProxy_GetISATargets: GetDisassemblyTargets(); break; default: RDCERR("Unexpected command"); return false; } @@ -2846,6 +2846,61 @@ ShaderReflection *ReplayProxy::GetShader(ResourceId id, string entryPoint) return m_ShaderReflectionCache[key]; } +string ReplayProxy::DisassembleShader(const ShaderReflection *refl, const string &target) +{ + string ret; + + ResourceId id; + std::string entryPoint; + std::string isatarget = target; + + if(refl) + { + id = refl->ID; + entryPoint = refl->EntryPoint; + } + + m_ToReplaySerialiser->Serialise("", id); + m_ToReplaySerialiser->Serialise("", entryPoint); + m_ToReplaySerialiser->Serialise("", isatarget); + + if(m_RemoteServer) + { + refl = m_Remote->GetShader(m_Remote->GetLiveID(id), entryPoint); + if(refl) + ret = m_Remote->DisassembleShader(m_Remote->GetShader(m_Remote->GetLiveID(id), entryPoint), + isatarget); + } + else + { + if(!SendReplayCommand(eReplayProxy_DisassembleShader)) + return ret; + } + + m_FromReplaySerialiser->Serialise("", ret); + + return ret; +} + +vector ReplayProxy::GetDisassemblyTargets() +{ + vector ret; + + if(m_RemoteServer) + { + ret = m_Remote->GetDisassemblyTargets(); + } + else + { + if(!SendReplayCommand(eReplayProxy_GetISATargets)) + return ret; + } + + m_FromReplaySerialiser->Serialise("", ret); + + return ret; +} + void ReplayProxy::FreeTargetResource(ResourceId id) { m_ToReplaySerialiser->Serialise("", id); diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 6b8dcfd59..15c2cf971 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -85,6 +85,9 @@ enum ReplayProxyPacket eReplayProxy_GetAPIProperties, eReplayProxy_PixelHistory, + + eReplayProxy_DisassembleShader, + eReplayProxy_GetISATargets, }; // This class implements IReplayDriver and StackResolver. On the local machine where the UI @@ -432,6 +435,9 @@ public: ShaderReflection *GetShader(ResourceId shader, string entryPoint); + vector GetDisassemblyTargets(); + string DisassembleShader(const ShaderReflection *refl, const string &target); + bool HasCallstacks(); void InitCallstackResolver(); Callstack::StackResolver *GetCallstackResolver(); diff --git a/renderdoc/driver/d3d11/d3d11_common.cpp b/renderdoc/driver/d3d11/d3d11_common.cpp index bf8b808c9..f142d5e90 100644 --- a/renderdoc/driver/d3d11/d3d11_common.cpp +++ b/renderdoc/driver/d3d11/d3d11_common.cpp @@ -477,8 +477,6 @@ ShaderReflection *MakeShaderReflection(DXBC::DXBCFile *dxbc) } } - ret->Disassembly = dxbc->GetDisassembly(); - if(dxbc->m_ShaderBlob.empty()) create_array_uninit(ret->RawBytes, 0); else diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index ea5c08a79..b032a0b9c 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -315,6 +315,27 @@ ShaderReflection *D3D11Replay::GetShader(ResourceId shader, string entryPoint) return ret; } +vector D3D11Replay::GetDisassemblyTargets() +{ + vector ret; + + ret.insert(ret.begin(), "DXBC"); + + return ret; +} + +string D3D11Replay::DisassembleShader(const ShaderReflection *refl, const string &target) +{ + auto it = WrappedShader::m_ShaderList.find(m_pDevice->GetResourceManager()->GetLiveID(refl->ID)); + + if(it == WrappedShader::m_ShaderList.end()) + return "Invalid Shader Specified"; + + DXBC::DXBCFile *dxbc = it->second->GetDXBC(); + + return dxbc->GetDisassembly(); +} + void D3D11Replay::FreeTargetResource(ResourceId id) { if(m_pDevice->GetResourceManager()->HasLiveResource(id)) diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index 93b33d0c4..7abfc6a03 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -58,6 +58,9 @@ public: ShaderReflection *GetShader(ResourceId shader, string entryPoint); + vector GetDisassemblyTargets(); + string DisassembleShader(const ShaderReflection *refl, const string &target); + vector GetUsage(ResourceId id); FrameRecord GetFrameRecord(); diff --git a/renderdoc/driver/d3d12/d3d12_common.cpp b/renderdoc/driver/d3d12/d3d12_common.cpp index b59a5a1ef..a1f724d4d 100644 --- a/renderdoc/driver/d3d12/d3d12_common.cpp +++ b/renderdoc/driver/d3d12/d3d12_common.cpp @@ -464,8 +464,6 @@ void MakeShaderReflection(DXBC::DXBCFile *dxbc, ShaderReflection *refl, } } - refl->Disassembly = dxbc->GetDisassembly(); - if(dxbc->m_ShaderBlob.empty()) create_array_uninit(refl->RawBytes, 0); else diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 43780c1e2..b2887250a 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -255,6 +255,27 @@ ShaderReflection *D3D12Replay::GetShader(ResourceId shader, string entryPoint) return NULL; } +vector D3D12Replay::GetDisassemblyTargets() +{ + vector ret; + + ret.insert(ret.begin(), "DXBC"); + + return ret; +} + +string D3D12Replay::DisassembleShader(const ShaderReflection *refl, const string &target) +{ + WrappedID3D12Shader *sh = m_pDevice->GetResourceManager()->GetLiveAs(refl->ID); + + if(!sh) + return "Invalid Shader Specified"; + + DXBC::DXBCFile *dxbc = sh->GetDXBC(); + + return dxbc->GetDisassembly(); +} + void D3D12Replay::FreeTargetResource(ResourceId id) { if(m_pDevice->GetResourceManager()->HasLiveResource(id)) diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 5c5f9e584..4c60c101c 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -56,6 +56,9 @@ public: ShaderReflection *GetShader(ResourceId shader, string entryPoint); + vector GetDisassemblyTargets(); + string DisassembleShader(const ShaderReflection *refl, const string &target); + vector GetUsage(ResourceId id); FrameRecord GetFrameRecord(); diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index b4b626b41..4e060d393 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -296,6 +296,7 @@ private: vector sources; vector includepaths; SPVModule spirv; + std::string disassembly; ShaderReflection reflection; GLuint prog; diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 209586ae7..f6da5d959 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -832,6 +832,32 @@ ShaderReflection *GLReplay::GetShader(ResourceId shader, string entryPoint) return &shaderDetails.reflection; } +vector GLReplay::GetDisassemblyTargets() +{ + vector ret; + + ret.insert(ret.begin(), "SPIR-V (RenderDoc)"); + + return ret; +} + +string GLReplay::DisassembleShader(const ShaderReflection *refl, const string &target) +{ + auto &shaderDetails = m_pDriver->m_Shaders[m_pDriver->GetResourceManager()->GetLiveID(refl->ID)]; + + if(shaderDetails.sources.empty()) + return "Invalid Shader Specified"; + + { + std::string &disasm = shaderDetails.disassembly; + + if(disasm.empty()) + disasm = shaderDetails.spirv.Disassemble(refl->EntryPoint.c_str()); + + return disasm; + } +} + void GLReplay::SavePipelineState() { GLPipe::State &pipe = m_CurPipelineState; diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index ae81b271e..c33b4fe5d 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -101,6 +101,9 @@ public: TextureDescription GetTexture(ResourceId id); ShaderReflection *GetShader(ResourceId shader, string entryPoint); + vector GetDisassemblyTargets(); + string DisassembleShader(const ShaderReflection *refl, const string &target); + vector GetDebugMessages(); vector GetUsage(ResourceId id); diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index 2f1e8ce37..6065da7bb 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -896,8 +896,6 @@ void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, { refl.DebugInfo.compileFlags = 0; - refl.Disassembly = ""; - if(shadType == eGL_COMPUTE_SHADER) { gl.glGetProgramiv(sepProg, eGL_COMPUTE_WORK_GROUP_SIZE, (GLint *)refl.DispatchThreadsDimension); diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index 0f2f66648..ff577a4fc 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -82,8 +82,6 @@ void WrappedOpenGL::ShaderData::Compile(WrappedOpenGL &gl, ResourceId id) reflection.ID = id; reflection.EntryPoint = "main"; - // for classic GL, entry point is always main - reflection.Disassembly = spirv.Disassemble("main"); // TODO sort these so that the first file contains the entry point create_array_uninit(reflection.DebugInfo.files, sources.size()); diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index 57d79413f..447721164 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -349,6 +349,7 @@ struct VulkanCreationInfo { uint32_t stage; string entryPoint; + string disassembly; ShaderReflection refl; ShaderBindpointMapping mapping; SPIRVPatchData patchData; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 7c868131f..2128cc65c 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -914,14 +914,38 @@ ShaderReflection *VulkanReplay::GetShader(ResourceId shader, string entryPoint) return NULL; } - // disassemble lazily on demand - if(shad->second.m_Reflections[entryPoint].refl.Disassembly.count == 0) - shad->second.m_Reflections[entryPoint].refl.Disassembly = - shad->second.spirv.Disassemble(entryPoint); - return &shad->second.m_Reflections[entryPoint].refl; } +vector VulkanReplay::GetDisassemblyTargets() +{ + vector ret; + + ret.insert(ret.begin(), "SPIR-V (RenderDoc)"); + + // could add canonical disassembly here if spirv-dis is available + // Ditto for SPIRV-cross (to glsl/hlsl) + + return ret; +} + +string VulkanReplay::DisassembleShader(const ShaderReflection *refl, const string &target) +{ + auto it = m_pDriver->m_CreationInfo.m_ShaderModule.find(GetResourceManager()->GetLiveID(refl->ID)); + + if(it == m_pDriver->m_CreationInfo.m_ShaderModule.end()) + return "Invalid Shader Specified"; + + { + std::string &disasm = it->second.m_Reflections[refl->EntryPoint.c_str()].disassembly; + + if(disasm.empty()) + disasm = it->second.spirv.Disassemble(refl->EntryPoint.c_str()); + + return disasm; + } +} + void VulkanReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace, uint32_t mip, uint32_t sample, CompType typeHint, float pixel[4]) { diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 3542360f4..b6a04a947 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -144,6 +144,9 @@ public: ShaderReflection *GetShader(ResourceId shader, string entryPoint); + vector GetDisassemblyTargets(); + string DisassembleShader(const ShaderReflection *refl, const string &target); + vector GetUsage(ResourceId id); FrameRecord GetFrameRecord(); diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 9fa1ca96e..78902af50 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -233,6 +233,24 @@ VKPipe::State ReplayController::GetVulkanPipelineState() return m_VulkanPipelineState; } +rdctype::array ReplayController::GetDisassemblyTargets() +{ + rdctype::array ret; + + vector targets = m_pDevice->GetDisassemblyTargets(); + + create_array_uninit(ret, targets.size()); + for(int32_t i = 0; i < ret.count; i++) + ret[i] = targets[i]; + + return ret; +} + +rdctype::str ReplayController::DisassembleShader(const ShaderReflection *refl, const char *target) +{ + return m_pDevice->DisassembleShader(refl, target); +} + FrameDescription ReplayController::GetFrameInfo() { return m_FrameRecord.frameInfo; @@ -1693,6 +1711,18 @@ ReplayRenderer_GetVulkanPipelineState(IReplayController *rend, VKPipe::State *st { *state = rend->GetVulkanPipelineState(); } +extern "C" RENDERDOC_API void RENDERDOC_CC +ReplayRenderer_GetDisassemblyTargets(IReplayController *rend, rdctype::array *targets) +{ + *targets = rend->GetDisassemblyTargets(); +} +extern "C" RENDERDOC_API void RENDERDOC_CC ReplayRenderer_DisassembleShader(IReplayController *rend, + ShaderReflection *refl, + const char *target, + rdctype::str *disasm) +{ + *disasm = rend->DisassembleShader(refl, target); +} extern "C" RENDERDOC_API void RENDERDOC_CC ReplayRenderer_BuildCustomShader( IReplayController *rend, const char *entry, const char *source, const uint32_t compileFlags, diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 7dbb6a43d..ba8960cca 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -142,6 +142,9 @@ public: GLPipe::State GetGLPipelineState(); VKPipe::State GetVulkanPipelineState(); + rdctype::array GetDisassemblyTargets(); + rdctype::str DisassembleShader(const ShaderReflection *refl, const char *target); + rdctype::pair BuildCustomShader(const char *entry, const char *source, const uint32_t compileFlags, ShaderStage type); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 322d3dd22..168e88cc7 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -92,6 +92,9 @@ public: virtual ShaderReflection *GetShader(ResourceId shader, string entryPoint) = 0; + virtual vector GetDisassemblyTargets() = 0; + virtual string DisassembleShader(const ShaderReflection *refl, const string &target) = 0; + virtual vector GetUsage(ResourceId id) = 0; virtual void SavePipelineState() = 0; diff --git a/renderdocui/Interop/ReplayRenderer.cs b/renderdocui/Interop/ReplayRenderer.cs index e7bd50a5c..5ca9b96e5 100644 --- a/renderdocui/Interop/ReplayRenderer.cs +++ b/renderdocui/Interop/ReplayRenderer.cs @@ -304,6 +304,11 @@ namespace renderdoc [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern void ReplayRenderer_GetVulkanPipelineState(IntPtr real, IntPtr mem); + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern void ReplayRenderer_GetDisassemblyTargets(IntPtr real, IntPtr targets); + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern void ReplayRenderer_DisassembleShader(IntPtr real, IntPtr refl, IntPtr target, IntPtr isa); + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern void ReplayRenderer_BuildCustomShader(IntPtr real, IntPtr entry, IntPtr source, UInt32 compileFlags, ShaderStageType type, ref ResourceId shaderID, IntPtr errorMem); [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] @@ -467,6 +472,35 @@ namespace renderdoc return ret; } + public string[] GetDisassemblyTargets() + { + IntPtr mem = CustomMarshal.Alloc(typeof(templated_array)); + + ReplayRenderer_GetDisassemblyTargets(m_Real, mem); + + string[] ret = CustomMarshal.TemplatedArrayToStringArray(mem, true); + + CustomMarshal.Free(mem); + + return ret; + } + + public string DisassembleShader(ShaderReflection refl, string target) + { + IntPtr disasmMem = CustomMarshal.Alloc(typeof(templated_array)); + IntPtr target_mem = CustomMarshal.MakeUTF8String(target); + + ReplayRenderer_DisassembleShader(m_Real, refl.origPtr, target_mem, disasmMem); + + string disasm = CustomMarshal.TemplatedArrayToString(disasmMem, true); + + CustomMarshal.Free(target_mem); + + CustomMarshal.Free(disasmMem); + + return disasm; + } + public ResourceId BuildCustomShader(string entry, string source, UInt32 compileFlags, ShaderStageType type, out string errors) { IntPtr mem = CustomMarshal.Alloc(typeof(templated_array)); diff --git a/renderdocui/Interop/Shader.cs b/renderdocui/Interop/Shader.cs index ebc969939..823b63137 100644 --- a/renderdocui/Interop/Shader.cs +++ b/renderdocui/Interop/Shader.cs @@ -426,9 +426,6 @@ namespace renderdoc [CustomMarshalAs(CustomUnmanagedType.CustomClass)] public ShaderDebugChunk DebugInfo; - [CustomMarshalAs(CustomUnmanagedType.UTF8TemplatedString)] - public string Disassembly; - [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] public byte[] RawBytes; @@ -516,4 +513,4 @@ namespace renderdoc [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] public BindpointMap[] ReadWriteResources; }; -}; \ No newline at end of file +}; diff --git a/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs b/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs index cf910b373..cc90e8708 100644 --- a/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs +++ b/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs @@ -2370,8 +2370,15 @@ namespace renderdocui.Windows.PipelineState { // use disassembly for now. It's not compilable GLSL but it's better than // starting with a blank canvas - files.Add("Disassembly", shaderDetails.Disassembly); - ShowShaderViewer(stage, files); + m_Core.Renderer.BeginInvoke((ReplayRenderer r) => + { + var disasm = r.DisassembleShader(shaderDetails, ""); + files.Add("Disassembly", disasm); + this.BeginInvoke((MethodInvoker)delegate + { + ShowShaderViewer(stage, files); + }); + }); } } diff --git a/renderdocui/Windows/ShaderViewer.cs b/renderdocui/Windows/ShaderViewer.cs index 10a65e3c2..33872097e 100644 --- a/renderdocui/Windows/ShaderViewer.cs +++ b/renderdocui/Windows/ShaderViewer.cs @@ -60,6 +60,7 @@ namespace renderdocui.Windows private D3D11PipelineState.ShaderStage m_Stage = null; private ShaderDebugTrace m_Trace = null; private ScintillaNET.Scintilla m_DisassemblyView = null; + private string m_DefaultDisasm = ""; private DockContent m_ErrorsDock = null; private DockContent m_ConstantsDock = null; @@ -484,54 +485,8 @@ namespace renderdocui.Windows else Text = m_Core.CurPipelineState.GetShaderName(stage); - var disasm = shader != null ? shader.Disassembly : ""; - - if (m_Core.Config.ShaderViewer_FriendlyNaming && m_ShaderDetails != null && - m_Core.APIProps.pipelineType.IsD3D()) { - for (int i = 0; i < m_ShaderDetails.ConstantBlocks.Length; i++) - { - var cbuf = m_ShaderDetails.ConstantBlocks[i]; - - var stem = string.Format("cb{0}", cbuf.bindPoint); - - if (cbuf.variables.Length == 0) - continue; - - disasm = FriendlyName(disasm, stem, "", cbuf.variables); - } - - foreach (var r in m_ShaderDetails.ReadOnlyResources) - { - if (r.IsSRV) - { - var needle = string.Format(", t{0}([^0-9])", r.bindPoint); - var replacement = string.Format(", {0}$1", r.name); - - Regex rgx = new Regex(needle); - disasm = rgx.Replace(disasm, replacement); - } - if (r.IsSampler) - { - var needle = string.Format(", s{0}([^0-9])", r.bindPoint); - var replacement = string.Format(", {0}$1", r.name); - - Regex rgx = new Regex(needle); - disasm = rgx.Replace(disasm, replacement); - } - } - foreach (var r in m_ShaderDetails.ReadWriteResources) - { - var needle = string.Format(", u{0}([^0-9])", r.bindPoint); - var replacement = string.Format(", {0}$1", r.name); - - Regex rgx = new Regex(needle); - disasm = rgx.Replace(disasm, replacement); - } - } - - { - m_DisassemblyView = MakeEditor("scintillaDisassem", disasm, m_Core.APIProps.pipelineType == GraphicsAPI.Vulkan); + m_DisassemblyView = MakeEditor("scintillaDisassem", "", m_Core.APIProps.pipelineType == GraphicsAPI.Vulkan); m_DisassemblyView.IsReadOnly = true; m_DisassemblyView.TabIndex = 0; @@ -568,9 +523,48 @@ namespace renderdocui.Windows m_DisassemblyView.MouseDown += new MouseEventHandler(contextMouseDown); } + TableLayoutPanel disasmLayoutPanel = new TableLayoutPanel(); + + ToolStrip disasmToolStrip = new ToolStrip(); + + ToolStripLabel disasmTypeLabel = new ToolStripLabel(); + disasmTypeLabel.Text = "Disassembly type: "; + + ToolStripComboBox disasmType = new ToolStripComboBox(); + + disasmType.DropDownStyle = ComboBoxStyle.DropDownList; + + // force creation of the handle so we can use BeginInvoke. + if (!IsHandleCreated) + CreateHandle(); + + m_Core.Renderer.BeginInvoke((ReplayRenderer r) => + { + string[] targets = r.GetDisassemblyTargets(); + + this.BeginInvoke((MethodInvoker)delegate + { + disasmType.Items.AddRange(targets); + disasmType.SelectedIndex = 0; + disasmType.SelectedIndexChanged += new System.EventHandler(disasmType_SelectedIndexChanged); + }); + }); + + disasmToolStrip.Dock = DockStyle.None; + disasmToolStrip.GripStyle = ToolStripGripStyle.Hidden; + disasmToolStrip.Items.AddRange(new ToolStripItem[] { disasmTypeLabel, disasmType }); + disasmToolStrip.Margin = new Padding(0, 0, 12, 0); + + disasmLayoutPanel.ColumnCount = 1; + disasmLayoutPanel.Controls.Add(disasmToolStrip, 0, 0); + disasmLayoutPanel.Controls.Add(m_DisassemblyView, 0, 1); + disasmLayoutPanel.RowCount = 2; + disasmLayoutPanel.RowStyles.Add(new RowStyle()); + disasmLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + m_Scintillas.Add(m_DisassemblyView); - var w = Helpers.WrapDockContent(dockPanel, m_DisassemblyView, "Disassembly"); + var w = Helpers.WrapDockContent(dockPanel, disasmLayoutPanel, "Disassembly"); w.DockState = DockState.Document; w.Show(); @@ -578,6 +572,68 @@ namespace renderdocui.Windows w.CloseButtonVisible = false; } + if (shader != null) + { + m_Core.Renderer.BeginInvoke((ReplayRenderer replay) => + { + var disasm = replay.DisassembleShader(m_ShaderDetails, ""); + + if (m_Core.Config.ShaderViewer_FriendlyNaming && m_ShaderDetails != null && + m_Core.APIProps.pipelineType.IsD3D()) + { + for (int i = 0; i < m_ShaderDetails.ConstantBlocks.Length; i++) + { + var cbuf = m_ShaderDetails.ConstantBlocks[i]; + + var stem = string.Format("cb{0}", cbuf.bindPoint); + + if (cbuf.variables.Length == 0) + continue; + + disasm = FriendlyName(disasm, stem, "", cbuf.variables); + } + + foreach (var r in m_ShaderDetails.ReadOnlyResources) + { + if (r.IsSRV) + { + var needle = string.Format(", t{0}([^0-9])", r.bindPoint); + var replacement = string.Format(", {0}$1", r.name); + + Regex rgx = new Regex(needle); + disasm = rgx.Replace(disasm, replacement); + } + if (r.IsSampler) + { + var needle = string.Format(", s{0}([^0-9])", r.bindPoint); + var replacement = string.Format(", {0}$1", r.name); + + Regex rgx = new Regex(needle); + disasm = rgx.Replace(disasm, replacement); + } + } + foreach (var r in m_ShaderDetails.ReadWriteResources) + { + var needle = string.Format(", u{0}([^0-9])", r.bindPoint); + var replacement = string.Format(", {0}$1", r.name); + + Regex rgx = new Regex(needle); + disasm = rgx.Replace(disasm, replacement); + } + } + + m_DefaultDisasm = disasm; + + this.BeginInvoke((MethodInvoker)delegate + { + m_DisassemblyView.IsReadOnly = false; + m_DisassemblyView.Text = m_DefaultDisasm; + m_DisassemblyView.UndoRedo.EmptyUndoBuffer(); + m_DisassemblyView.IsReadOnly = true; + }); + }); + } + if (shader != null && shader.DebugInfo.files.Length > 0) { if(trace != null) @@ -713,6 +769,37 @@ namespace renderdocui.Windows this.ResumeLayout(false); } + private void disasmType_SelectedIndexChanged(object sender, EventArgs e) + { + ToolStripComboBox disasmType = sender as ToolStripComboBox; + if (disasmType == null) + return; + + if (disasmType.SelectedIndex == 0 || m_ShaderDetails == null) + { + m_DisassemblyView.IsReadOnly = false; + m_DisassemblyView.Text = m_DefaultDisasm; + m_DisassemblyView.UndoRedo.EmptyUndoBuffer(); + m_DisassemblyView.IsReadOnly = true; + } + else + { + string target = disasmType.Items[disasmType.SelectedIndex].ToString(); + m_Core.Renderer.BeginInvoke((ReplayRenderer r) => + { + string disasm = r.DisassembleShader(m_ShaderDetails, target); + + this.BeginInvoke((MethodInvoker)delegate + { + m_DisassemblyView.IsReadOnly = false; + m_DisassemblyView.Text = disasm; + m_DisassemblyView.UndoRedo.EmptyUndoBuffer(); + m_DisassemblyView.IsReadOnly = true; + }); + }); + } + } + private ListBox m_FileList = null; void list_SelectedIndexChanged(object sender, EventArgs e)