From fe4fa7138d7ae87e02bb7c399e3ae429913a1d16 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 22 Dec 2017 16:12:52 +0000 Subject: [PATCH] Move the GCN disassembly to the replay controller * This way it's uniformly supported, so e.g. we can still get it when the replay itself is running on android. * To do this we just pass the 'raw' bytes, which contains the whole blob needed for disassembly. This means a little extra work to disassemble D3D blobs by doing a tiny parse of the DXBC. --- renderdoc/driver/d3d11/d3d11_replay.cpp | 13 ++-- renderdoc/driver/d3d12/d3d12_replay.cpp | 13 ++-- renderdoc/driver/gl/gl_replay.cpp | 18 ++---- renderdoc/driver/ihv/amd/amd_isa.cpp | 73 ++++++++++++++-------- renderdoc/driver/ihv/amd/amd_isa.h | 4 +- renderdoc/driver/ihv/amd/amd_isa_posix.cpp | 7 ++- renderdoc/driver/ihv/amd/amd_isa_win32.cpp | 71 ++++++++++++++++++--- renderdoc/driver/vulkan/vk_replay.cpp | 22 +++---- renderdoc/replay/replay_controller.cpp | 13 ++++ renderdoc/replay/replay_controller.h | 3 + 10 files changed, 163 insertions(+), 74 deletions(-) diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 77dd12bd9..2697e28d8 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -25,7 +25,6 @@ #include "d3d11_replay.h" #include "driver/dx/official/d3dcompiler.h" -#include "driver/ihv/amd/amd_isa.h" #include "driver/shaders/dxbc/dxbc_debug.h" #include "serialise/rdcfile.h" #include "strings/string_utils.h" @@ -35,6 +34,8 @@ #include "d3d11_renderstate.h" #include "d3d11_resources.h" +static const char *DXBCDisassemblyTarget = "DXBC"; + D3D11Replay::D3D11Replay() { m_pDevice = NULL; @@ -256,10 +257,8 @@ vector D3D11Replay::GetDisassemblyTargets() { vector ret; - GCNISA::GetTargets(GraphicsAPI::D3D11, ret); - // DXBC is always first - ret.insert(ret.begin(), "DXBC"); + ret.insert(ret.begin(), DXBCDisassemblyTarget); return ret; } @@ -271,14 +270,14 @@ string D3D11Replay::DisassembleShader(ResourceId pipeline, const ShaderReflectio WrappedShader::m_ShaderList.find(m_pDevice->GetResourceManager()->GetLiveID(refl->resourceId)); if(it == WrappedShader::m_ShaderList.end()) - return "Invalid Shader Specified"; + return "; Invalid Shader Specified"; DXBC::DXBCFile *dxbc = it->second->GetDXBC(); - if(target == "DXBC" || target.empty()) + if(target == DXBCDisassemblyTarget || target.empty()) return dxbc->GetDisassembly(); - return GCNISA::Disassemble(dxbc, target); + return StringFormat::Fmt("; Invalid disassembly target %s", target.c_str()); } void D3D11Replay::FreeTargetResource(ResourceId id) diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 11f9b2909..04c0b611c 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -25,12 +25,13 @@ #include "d3d12_replay.h" #include "driver/dx/official/d3dcompiler.h" #include "driver/dxgi/dxgi_common.h" -#include "driver/ihv/amd/amd_isa.h" #include "serialise/rdcfile.h" #include "d3d12_command_queue.h" #include "d3d12_device.h" #include "d3d12_resources.h" +static const char *DXBCDisassemblyTarget = "DXBC"; + template T &resize_and_add(rdcarray &vec, size_t idx) { @@ -262,10 +263,8 @@ vector D3D12Replay::GetDisassemblyTargets() { vector ret; - GCNISA::GetTargets(GraphicsAPI::D3D12, ret); - // DXBC is always first - ret.insert(ret.begin(), "DXBC"); + ret.insert(ret.begin(), DXBCDisassemblyTarget); return ret; } @@ -277,14 +276,14 @@ string D3D12Replay::DisassembleShader(ResourceId pipeline, const ShaderReflectio m_pDevice->GetResourceManager()->GetLiveAs(refl->resourceId); if(!sh) - return "Invalid Shader Specified"; + return "; Invalid Shader Specified"; DXBC::DXBCFile *dxbc = sh->GetDXBC(); - if(target == "DXBC" || target.empty()) + if(target == DXBCDisassemblyTarget || target.empty()) return dxbc->GetDisassembly(); - return GCNISA::Disassemble(dxbc, target); + return StringFormat::Fmt("; Invalid disassembly target %s", target.c_str()); } void D3D12Replay::FreeTargetResource(ResourceId id) diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 35149f99b..1954b7b66 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -24,7 +24,6 @@ ******************************************************************************/ #include "gl_replay.h" -#include "driver/ihv/amd/amd_isa.h" #include "maths/matrix.h" #include "serialise/rdcfile.h" #include "strings/string_utils.h" @@ -34,6 +33,8 @@ #define OPENGL 1 #include "data/glsl/debuguniforms.h" +static const char *SPIRVDisassemblyTarget = "SPIR-V (RenderDoc)"; + GLReplay::GLReplay() { m_pDriver = NULL; @@ -792,10 +793,8 @@ vector GLReplay::GetDisassemblyTargets() { vector ret; - GCNISA::GetTargets(GraphicsAPI::OpenGL, ret); - // default is always first - ret.insert(ret.begin(), "SPIR-V (RenderDoc)"); + ret.insert(ret.begin(), SPIRVDisassemblyTarget); return ret; } @@ -807,9 +806,9 @@ string GLReplay::DisassembleShader(ResourceId pipeline, const ShaderReflection * m_pDriver->m_Shaders[m_pDriver->GetResourceManager()->GetLiveID(refl->resourceId)]; if(shaderDetails.sources.empty()) - return "Invalid Shader Specified"; + return "; Invalid Shader Specified"; - if(target == "SPIR-V (RenderDoc)" || target.empty()) + if(target == SPIRVDisassemblyTarget || target.empty()) { std::string &disasm = shaderDetails.disassembly; @@ -819,12 +818,7 @@ string GLReplay::DisassembleShader(ResourceId pipeline, const ShaderReflection * return disasm; } - ShaderStage stages[] = { - ShaderStage::Vertex, ShaderStage::Tess_Control, ShaderStage::Tess_Eval, - ShaderStage::Geometry, ShaderStage::Fragment, ShaderStage::Compute, - }; - - return GCNISA::Disassemble(stages[ShaderIdx(shaderDetails.type)], shaderDetails.sources, target); + return StringFormat::Fmt("; Invalid disassembly target %s", target.c_str()); } void GLReplay::SavePipelineState() diff --git a/renderdoc/driver/ihv/amd/amd_isa.cpp b/renderdoc/driver/ihv/amd/amd_isa.cpp index e55373189..67c1c1bc6 100644 --- a/renderdoc/driver/ihv/amd/amd_isa.cpp +++ b/renderdoc/driver/ihv/amd/amd_isa.cpp @@ -41,9 +41,12 @@ static const std::string virtualcontext_name = "VirtualContext"; std::string pluginPath = "amd/isa"; -static bool IsSupported(GraphicsAPI api) +// in amd_isa_.cpp +std::string DisassembleDXBC(const bytebuf &shaderBytes, const std::string &target); + +static bool IsSupported(ShaderEncoding encoding) { - if(api == GraphicsAPI::OpenGL) + if(encoding == ShaderEncoding::GLSL) { std::string vc = LocatePluginFile(pluginPath, virtualcontext_name); @@ -57,7 +60,7 @@ static bool IsSupported(GraphicsAPI api) return true; } - if(api == GraphicsAPI::Vulkan) + if(encoding == ShaderEncoding::SPIRV) { // TODO need to check if an AMD context is running std::string amdspv = LocatePluginFile(pluginPath, amdspv_name); @@ -73,10 +76,9 @@ static bool IsSupported(GraphicsAPI api) } // we only need to check if we can get atidxx64.dll - if(api == GraphicsAPI::D3D11 || api == GraphicsAPI::D3D12) + if(encoding == ShaderEncoding::DXBC) { - DXBC::DXBCFile *dummy = NULL; - std::string test = Disassemble(dummy, ""); + std::string test = DisassembleDXBC(bytebuf(), ""); return test.empty(); } @@ -88,7 +90,25 @@ void GetTargets(GraphicsAPI api, std::vector &targets) { targets.reserve(asicCount + 1); - if(IsSupported(api)) + ShaderEncoding primary = ShaderEncoding::SPIRV, secondary = ShaderEncoding::SPIRV; + + if(IsD3D(api)) + { + primary = ShaderEncoding::DXBC; + secondary = ShaderEncoding::DXBC; + } + else if(api == GraphicsAPI::OpenGL) + { + primary = ShaderEncoding::GLSL; + secondary = ShaderEncoding::SPIRV; + } + else if(api == GraphicsAPI::Vulkan) + { + primary = ShaderEncoding::SPIRV; + secondary = ShaderEncoding::SPIRV; + } + + if(IsSupported(primary) || IsSupported(secondary)) { // OpenGL doesn't support AMDIL if(api != GraphicsAPI::OpenGL) @@ -105,9 +125,9 @@ void GetTargets(GraphicsAPI api, std::vector &targets) } } -std::string Disassemble(const SPVModule *spv, const std::string &entry, const std::string &target) +std::string DisassembleSPIRV(ShaderStage stage, const bytebuf &shaderBytes, const std::string &target) { - if(!IsSupported(GraphicsAPI::Vulkan)) + if(!IsSupported(ShaderEncoding::SPIRV)) { return "; SPIR-V disassembly not supported, couldn't locate " + amdspv_name + R"( ; Normally it's in plugins/amd/isa/ in your build - if you are building locally you'll need to @@ -146,8 +166,6 @@ std::string Disassemble(const SPVModule *spv, const std::string &entry, const st if(!found) return "; Invalid ISA Target specified"; - ShaderStage stage = spv->StageForEntry(entry); - const char *stageName = "unk"; switch(stage) @@ -171,7 +189,7 @@ std::string Disassemble(const SPVModule *spv, const std::string &entry, const st tempPath.c_str(), stageName, tempPath.c_str(), stageName, tempPath.c_str(), stageName, tempPath.c_str(), stageName, tempPath.c_str(), tempPath.c_str()); - FileIO::dump(inPath.c_str(), spv->spirv.data(), spv->spirv.size() * sizeof(uint32_t)); + FileIO::dump(inPath.c_str(), shaderBytes.data(), shaderBytes.size()); // try to locate the amdspv relative to our running program std::string amdspv = LocatePluginFile(pluginPath, amdspv_name); @@ -226,10 +244,9 @@ std::string Disassemble(const SPVModule *spv, const std::string &entry, const st return ret; } -std::string Disassemble(ShaderStage stage, const std::vector &glsl, - const std::string &target) +std::string DisassembleGLSL(ShaderStage stage, const bytebuf &shaderBytes, const std::string &target) { - if(!IsSupported(GraphicsAPI::OpenGL)) + if(!IsSupported(ShaderEncoding::GLSL)) { return R"(; GLSL disassembly not supported, couldn't locate VirtualContext.exe or it failed to run. ; It only works when the AMD driver is currently being used for graphics. @@ -325,16 +342,7 @@ std::string Disassemble(ShaderStage stage, const std::vector &glsl, cmdLine += ";\""; - std::string source; - - // concatenate the source files together - for(const std::string &s : glsl) - { - source += s; - source += "\n"; - } - - FileIO::dump(inPath.c_str(), source.data(), source.size()); + FileIO::dump(inPath.c_str(), shaderBytes.data(), shaderBytes.size()); // try to locate the amdspv relative to our running program std::string vc = LocatePluginFile(pluginPath, virtualcontext_name); @@ -377,4 +385,19 @@ std::string Disassemble(ShaderStage stage, const std::vector &glsl, return ret; } +std::string Disassemble(ShaderEncoding encoding, ShaderStage stage, const bytebuf &shaderBytes, + const std::string &target) +{ + if(encoding == ShaderEncoding::DXBC) + return DisassembleDXBC(shaderBytes, target); + + if(encoding == ShaderEncoding::SPIRV) + return DisassembleSPIRV(stage, shaderBytes, target); + + if(encoding == ShaderEncoding::GLSL) + return DisassembleGLSL(stage, shaderBytes, target); + + return StringFormat::Fmt("Unsupported encoding for shader '%s'", ToStr(encoding).c_str()); +} + }; // namespace GCNISA diff --git a/renderdoc/driver/ihv/amd/amd_isa.h b/renderdoc/driver/ihv/amd/amd_isa.h index a1fef02a2..9a4022b2f 100644 --- a/renderdoc/driver/ihv/amd/amd_isa.h +++ b/renderdoc/driver/ihv/amd/amd_isa.h @@ -38,8 +38,6 @@ namespace GCNISA { void GetTargets(GraphicsAPI api, std::vector &targets); -std::string Disassemble(const DXBC::DXBCFile *dxbc, const std::string &target); -std::string Disassemble(const SPVModule *spv, const std::string &entry, const std::string &target); -std::string Disassemble(ShaderStage stage, const std::vector &glsl, +std::string Disassemble(ShaderEncoding api, ShaderStage stage, const bytebuf &shaderBytes, const std::string &target); }; \ No newline at end of file diff --git a/renderdoc/driver/ihv/amd/amd_isa_posix.cpp b/renderdoc/driver/ihv/amd/amd_isa_posix.cpp index 45724ee17..8ecebc479 100644 --- a/renderdoc/driver/ihv/amd/amd_isa_posix.cpp +++ b/renderdoc/driver/ihv/amd/amd_isa_posix.cpp @@ -24,7 +24,10 @@ #include "amd_isa.h" -std::string GCNISA::Disassemble(const DXBC::DXBCFile *dxbc, const std::string &target) +namespace GCNISA +{ +std::string DisassembleDXBC(const bytebuf &shaderBytes, const std::string &target) { return "Disassembling D3D shaders is only supported on windows"; -} \ No newline at end of file +} +}; \ No newline at end of file diff --git a/renderdoc/driver/ihv/amd/amd_isa_win32.cpp b/renderdoc/driver/ihv/amd/amd_isa_win32.cpp index e10a1b2e2..074e6317f 100644 --- a/renderdoc/driver/ihv/amd/amd_isa_win32.cpp +++ b/renderdoc/driver/ihv/amd/amd_isa_win32.cpp @@ -23,8 +23,8 @@ ******************************************************************************/ #include "amd_isa.h" +#include "common/common.h" #include "core/plugins.h" -#include "driver/shaders/dxbc/dxbc_inspect.h" #include "official/RGA/Common/AmdDxGsaCompile.h" #include "official/RGA/elf/elf32.h" #include "amd_isa_devices.h" @@ -46,7 +46,6 @@ https://github.com/baldurk/renderdoc/wiki/GCN-ISA)"; namespace GCNISA { extern std::string pluginPath; -}; static HMODULE GetAMDModule() { @@ -60,7 +59,7 @@ static HMODULE GetAMDModule() return module; } -std::string GCNISA::Disassemble(const DXBC::DXBCFile *dxbc, const std::string &target) +std::string DisassembleDXBC(const bytebuf &shaderBytes, const std::string &target) { HMODULE mod = GetAMDModule(); @@ -73,9 +72,9 @@ std::string GCNISA::Disassemble(const DXBC::DXBCFile *dxbc, const std::string &t ; To see instructions on how to download and configure it on your system, go to: ; https://github.com/baldurk/renderdoc/wiki/GCN-ISA)"; - // if DXBC is NULL we're testing support, so return empty string - indicating no error + // if shaderBytes is empty we're testing support, so return empty string - indicating no error // initialising - if(dxbc == NULL || target == "") + if(shaderBytes.empty() || target == "") return ""; PfnAmdDxGsaCompileShader compileShader = @@ -114,8 +113,65 @@ std::string GCNISA::Disassemble(const DXBC::DXBCFile *dxbc, const std::string &t if(in.chipFamily == 0) return "; Invalid ISA Target specified"; - in.pShaderByteCode = dxbc->m_HexDump.data(); - in.byteCodeLength = dxbc->m_HexDump.size(); + // we do a little mini parse of the DXBC file, just enough to get the shader code out. This is + // because we're getting called from outside the D3D backend where the shader bytes are opaque. + + const char *dxbcParseError = "; Failed to fetch D3D shader code from DXBC"; + + const byte *base = shaderBytes.data(); + const uint32_t *end = (const uint32_t *)(base + shaderBytes.size()); + const uint32_t *dxbc = (const uint32_t *)base; + + if(*dxbc != MAKE_FOURCC('D', 'X', 'B', 'C')) + return dxbcParseError; + + dxbc++; // fourcc + dxbc += 4; // hash + dxbc++; // unknown + dxbc++; // fileLength + + if(dxbc >= end) + return dxbcParseError; + + const uint32_t numChunks = *dxbc; + dxbc++; + + std::vector chunkOffsets; + for(uint32_t i = 0; i < numChunks; i++) + { + if(dxbc >= end) + return dxbcParseError; + + chunkOffsets.push_back(*dxbc); + dxbc++; + } + + in.pShaderByteCode = NULL; + in.byteCodeLength = 0; + + for(uint32_t offs : chunkOffsets) + { + dxbc = (const uint32_t *)(base + offs); + + if(dxbc + 2 >= end) + return dxbcParseError; + + if(*dxbc == MAKE_FOURCC('S', 'H', 'E', 'X') || *dxbc == MAKE_FOURCC('S', 'H', 'D', 'R')) + { + dxbc++; + in.byteCodeLength = *dxbc; + dxbc++; + in.pShaderByteCode = dxbc; + + if(dxbc + in.byteCodeLength >= end) + return dxbcParseError; + + break; + } + } + + if(in.byteCodeLength == 0) + return dxbcParseError; out.size = sizeof(out); @@ -194,3 +250,4 @@ std::string GCNISA::Disassemble(const DXBC::DXBCFile *dxbc, const std::string &t return ret; } +}; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index a1ffefc1b..d6f6a5fce 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -24,7 +24,6 @@ #include "vk_replay.h" #include -#include "driver/ihv/amd/amd_isa.h" #include "maths/camera.h" #include "maths/matrix.h" #include "serialise/rdcfile.h" @@ -36,6 +35,9 @@ #define VULKAN 1 #include "data/glsl/debuguniforms.h" +static const char *SPIRVDisassemblyTarget = "SPIR-V (RenderDoc)"; +static const char *LiveDriverDisassemblyTarget = "Live driver disassembly"; + VulkanReplay::OutputWindow::OutputWindow() : m_WindowSystem(WindowingSystem::Unknown), width(0), height(0) { @@ -951,12 +953,10 @@ vector VulkanReplay::GetDisassemblyTargets() const VkLayerDispatchTable *vt = ObjDisp(dev); if(vt->GetShaderInfoAMD) - ret.push_back("Live driver disassembly"); - - GCNISA::GetTargets(GraphicsAPI::Vulkan, ret); + ret.push_back(LiveDriverDisassemblyTarget); // default is always first - ret.insert(ret.begin(), "SPIR-V (RenderDoc)"); + ret.insert(ret.begin(), SPIRVDisassemblyTarget); // could add canonical disassembly here if spirv-dis is available // Ditto for SPIRV-cross (to glsl/hlsl) @@ -971,9 +971,9 @@ string VulkanReplay::DisassembleShader(ResourceId pipeline, const ShaderReflecti GetResourceManager()->GetLiveID(refl->resourceId)); if(it == m_pDriver->m_CreationInfo.m_ShaderModule.end()) - return "Invalid Shader Specified"; + return "; Invalid Shader Specified"; - if(target == "SPIR-V (RenderDoc)" || target.empty()) + if(target == SPIRVDisassemblyTarget || target.empty()) { std::string &disasm = it->second.m_Reflections[refl->entryPoint.c_str()].disassembly; @@ -986,12 +986,12 @@ string VulkanReplay::DisassembleShader(ResourceId pipeline, const ShaderReflecti VkDevice dev = m_pDriver->GetDev(); const VkLayerDispatchTable *vt = ObjDisp(dev); - if(vt->GetShaderInfoAMD) + if(target == LiveDriverDisassemblyTarget && vt->GetShaderInfoAMD) { if(pipeline == ResourceId()) { - return "No pipeline specified, live driver disassembly is not available\n" - "Shader must be disassembled with a specific pipeline to get live driver assembly."; + return "; No pipeline specified, live driver disassembly is not available\n" + "; Shader must be disassembled with a specific pipeline to get live driver assembly."; } VkPipeline pipe = m_pDriver->GetResourceManager()->GetLiveHandle(pipeline); @@ -1011,7 +1011,7 @@ string VulkanReplay::DisassembleShader(ResourceId pipeline, const ShaderReflecti return disasm; } - return GCNISA::Disassemble(&it->second.spirv, refl->entryPoint.c_str(), target); + return StringFormat::Fmt("; Invalid disassembly target %s", target.c_str()); } void VulkanReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace, diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index c9b2aa860..8c59f419f 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -27,6 +27,7 @@ #include #include #include "common/dds_readwrite.h" +#include "driver/ihv/amd/amd_isa.h" #include "jpeg-compressor/jpgd.h" #include "jpeg-compressor/jpge.h" #include "maths/formatpacking.h" @@ -249,12 +250,19 @@ rdcarray ReplayController::GetDisassemblyTargets() for(const std::string &t : targets) ret.push_back(t); + for(const std::string &t : m_GCNTargets) + ret.push_back(t); + return ret; } rdcstr ReplayController::DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, const char *target) { + for(const std::string &t : m_GCNTargets) + if(t == target) + return GCNISA::Disassemble(refl->encoding, refl->stage, refl->rawBytes, target); + return m_pDevice->DisassembleShader(pipeline, refl, target); } @@ -1594,6 +1602,11 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd FetchPipelineState(); + m_APIProps = m_pDevice->GetAPIProperties(); + + // fetch GCN ISA targets + GCNISA::GetTargets(m_APIProps.pipelineType, m_GCNTargets); + { std::vector ids = m_pDevice->GetBuffers(); diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index f4ec2f606..705347640 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -210,6 +210,9 @@ private: FrameRecord m_FrameRecord; vector m_Drawcalls; + APIProperties m_APIProps; + std::vector m_GCNTargets; + volatile int32_t m_ReplayLoopCancel = 0; volatile int32_t m_ReplayLoopFinished = 0;