diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index 87d7bc958..bd3540127 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -178,6 +178,7 @@ void PersistantConfig::applyValues(const QVariantMap &values) RENAMED_SETTING(QString, LastLogPath, LastCaptureFilePath); RENAMED_SETTING(QVariantList, RecentLogFiles, RecentCaptureFiles); RENAMED_SETTING(QDateTime, DegradedLog_LastUpdate, DegradedCapture_LastUpdate); + RENAMED_SETTING(QVariantList, SPIRVDisassemblers, ShaderProcessors); } int PersistantConfig::RemoteHostCount() @@ -310,25 +311,25 @@ bool PersistantConfig::Load(const rdcstr &filename) RemoteHosts.insert(0, host); } - bool tools[arraydim()] = {}; + bool tools[arraydim()] = {}; // see which known tools are registered - for(const SPIRVDisassembler &dis : SPIRVDisassemblers) + for(const ShaderProcessingTool &dis : ShaderProcessors) { // if it's declared - if(dis.tool != KnownSPIRVTool::Unknown) + if(dis.tool != KnownShaderTool::Unknown) tools[(size_t)dis.tool] = true; - for(KnownSPIRVTool tool : values()) + for(KnownShaderTool tool : values()) { if(QString(dis.executable).contains(ToolExecutable(tool))) tools[(size_t)tool] = true; } } - for(KnownSPIRVTool tool : values()) + for(KnownShaderTool tool : values()) { - if(tool == KnownSPIRVTool::Unknown || tools[(size_t)tool]) + if(tool == KnownShaderTool::Unknown || tools[(size_t)tool]) continue; QString exe = ToolExecutable(tool); @@ -341,14 +342,14 @@ bool PersistantConfig::Load(const rdcstr &filename) if(!path.isEmpty()) { - SPIRVDisassembler dis; - dis.name = ToQStr(tool); + ShaderProcessingTool s; + s.name = ToQStr(tool); // we store just the base name, so when we launch the process it will always find it in PATH, // rather than baking in the current PATH result. - dis.executable = exe; - dis.tool = tool; + s.executable = exe; + s.tool = tool; - SPIRVDisassemblers.push_back(dis); + ShaderProcessors.push_back(s); continue; } @@ -359,10 +360,15 @@ bool PersistantConfig::Load(const rdcstr &filename) QStringList searchPaths = {appDir.absoluteFilePath(lit("plugins/spirv/"))}; #if defined(Q_OS_WIN64) + // windows local searchPaths << appDir.absoluteFilePath(lit("../../plugins-win64/spirv/")); -#elif defined(Q_OS_WIN64) +#elif defined(Q_OS_WIN32) + // windows local searchPaths << appDir.absoluteFilePath(lit("../../plugins-win32/spirv/")); #elif defined(Q_OS_LINUX) + // linux installation + searchPaths << appDir.absoluteFilePath(lit("../share/renderdoc/plugins/spirv/")); + // linux local searchPaths << appDir.absoluteFilePath(lit("../../plugins-linux64/spirv/")); #endif @@ -372,17 +378,29 @@ bool PersistantConfig::Load(const rdcstr &filename) if(!path.isEmpty()) { - SPIRVDisassembler dis; - dis.name = ToQStr(tool); - dis.executable = path; - dis.tool = tool; + ShaderProcessingTool s; + s.name = ToQStr(tool); + s.executable = path; + s.tool = tool; - SPIRVDisassemblers.push_back(dis); + ShaderProcessors.push_back(s); continue; } } + // sanitisation pass, if a tool is declared as a known type ensure its inputs/outputs are correct. + // This is mostly for backwards compatibility with configs from before the inputs/outputs were + // added. + for(ShaderProcessingTool &dis : ShaderProcessors) + { + if(dis.tool != KnownShaderTool::Unknown) + { + dis.input = ToolInput(dis.tool); + dis.output = ToolOutput(dis.tool); + } + } + return ret; } @@ -467,20 +485,64 @@ rdcstr PersistantConfig::GetConfigSetting(const rdcstr &name) return ""; } -SPIRVDisassembler::SPIRVDisassembler(const QVariant &var) +ShaderProcessingTool::ShaderProcessingTool(const QVariant &var) { QVariantMap map = var.toMap(); if(map.contains(lit("tool"))) - tool = (KnownSPIRVTool)map[lit("tool")].toUInt(); + tool = (KnownShaderTool)map[lit("tool")].toUInt(); if(map.contains(lit("name"))) name = map[lit("name")].toString(); if(map.contains(lit("executable"))) executable = map[lit("executable")].toString(); if(map.contains(lit("args"))) - args = map[lit("args")].toString(); + { + QString a = map[lit("args")].toString(); + + // backwards compatibility + a.replace(lit("{spv_disasm}"), lit("{output_file}")); + a.replace(lit("{spv_bin}"), lit("{input_file}")); + + args = a; + } + + if(map.contains(lit("input"))) + { + input = (ShaderEncoding)map[lit("input")].toUInt(); + } + else + { + // backwards compatibility, it's a SPIR-V disassembler + input = ShaderEncoding::SPIRV; + } + + if(map.contains(lit("output"))) + { + output = (ShaderEncoding)map[lit("output")].toUInt(); + } + else + { + // backwards compatibility, we have to guess, assume GLSL as a sensible default + output = ShaderEncoding::GLSL; + } } -SPIRVDisassembler::operator QVariant() const +rdcstr ShaderProcessingTool::DefaultArguments() const +{ + if(tool == KnownShaderTool::SPIRV_Cross) + return "--output {output_file} {input_file} --vulkan-semantics"; + else if(tool == KnownShaderTool::spirv_dis) + return "--no-color -o {output_file} {input_file}"; + else if(tool == KnownShaderTool::glslangValidatorGLSL) + return "-g -V -o {output_file} {input_file} -S {glsl_stage4}"; + else if(tool == KnownShaderTool::glslangValidatorHLSL) + return "-D -g -V -o {output_file} {input_file} -S {glsl_stage4} -e {entry_point}"; + else if(tool == KnownShaderTool::spirv_as) + return "-o {output_file} {input_file}"; + + return args; +} + +ShaderProcessingTool::operator QVariant() const { QVariantMap map; @@ -488,6 +550,8 @@ SPIRVDisassembler::operator QVariant() const map[lit("name")] = name; map[lit("executable")] = executable; map[lit("args")] = args; + map[lit("input")] = (uint32_t)input; + map[lit("output")] = (uint32_t)output; return map; } diff --git a/qrenderdoc/Code/Interface/PersistantConfig.h b/qrenderdoc/Code/Interface/PersistantConfig.h index 63c973129..1c944485b 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.h +++ b/qrenderdoc/Code/Interface/PersistantConfig.h @@ -28,7 +28,7 @@ #include "QRDInterface.h" #include "RemoteHost.h" -DOCUMENT(R"(Identifies a particular known SPIR-V tool used for disassembly. +DOCUMENT(R"(Identifies a particular known tool used for shader processing. .. data:: Unknown @@ -41,39 +41,94 @@ DOCUMENT(R"(Identifies a particular known SPIR-V tool used for disassembly. .. data:: spirv_dis `spirv-dis from SPIRV-Tools `_. + +.. data:: glslangValidatorGLSL + + `glslang compiler (GLSL) `_. + +.. data:: glslangValidatorHLSL + + `glslang compiler (HLSL) `_. + +.. data:: spirv_as + + `spirv-as from SPIRV-Tools `_. + )"); -enum class KnownSPIRVTool : uint32_t +enum class KnownShaderTool : uint32_t { Unknown, First = Unknown, SPIRV_Cross, spirv_dis, + glslangValidatorGLSL, + glslangValidatorHLSL, + spirv_as, Count, }; -ITERABLE_OPERATORS(KnownSPIRVTool); +ITERABLE_OPERATORS(KnownShaderTool); -inline rdcstr ToolExecutable(KnownSPIRVTool tool) +inline rdcstr ToolExecutable(KnownShaderTool tool) { - if(tool == KnownSPIRVTool::SPIRV_Cross) + if(tool == KnownShaderTool::SPIRV_Cross) return "spirv-cross"; - else if(tool == KnownSPIRVTool::spirv_dis) + else if(tool == KnownShaderTool::spirv_dis) return "spirv-dis"; + else if(tool == KnownShaderTool::glslangValidatorGLSL) + return "glslangValidator"; + else if(tool == KnownShaderTool::glslangValidatorHLSL) + return "glslangValidator"; + else if(tool == KnownShaderTool::spirv_as) + return "spirv-as"; return ""; } -DOCUMENT("Describes an external program that can be used to disassemble SPIR-V."); -struct SPIRVDisassembler +inline ShaderEncoding ToolInput(KnownShaderTool tool) +{ + if(tool == KnownShaderTool::SPIRV_Cross || tool == KnownShaderTool::spirv_dis) + return ShaderEncoding::SPIRV; + else if(tool == KnownShaderTool::glslangValidatorGLSL) + return ShaderEncoding::GLSL; + else if(tool == KnownShaderTool::glslangValidatorHLSL) + return ShaderEncoding::HLSL; + else if(tool == KnownShaderTool::spirv_as) + return ShaderEncoding::SPIRVAsm; + + return ShaderEncoding::Unknown; +} + +inline ShaderEncoding ToolOutput(KnownShaderTool tool) +{ + if(tool == KnownShaderTool::SPIRV_Cross) + return ShaderEncoding::GLSL; + else if(tool == KnownShaderTool::spirv_dis) + return ShaderEncoding::SPIRVAsm; + else if(tool == KnownShaderTool::glslangValidatorGLSL || + tool == KnownShaderTool::glslangValidatorHLSL || tool == KnownShaderTool::spirv_as) + return ShaderEncoding::SPIRV; + + return ShaderEncoding::Unknown; +} + +DOCUMENT(R"(Describes an external program that can be used to process shaders, typically either +compiling from a high-level language to a binary format, or decompiling from the binary format to +a high-level language or textual representation. + +Commonly used with SPIR-V. +)"); +struct ShaderProcessingTool { DOCUMENT(""); - SPIRVDisassembler() = default; - VARIANT_CAST(SPIRVDisassembler); - bool operator==(const SPIRVDisassembler &o) const + ShaderProcessingTool() = default; + VARIANT_CAST(ShaderProcessingTool); + bool operator==(const ShaderProcessingTool &o) const { - return tool == o.tool && name == o.name && executable == o.executable && args == o.args; + return tool == o.tool && name == o.name && executable == o.executable && args == o.args && + input == o.input && output == o.output; } - bool operator<(const SPIRVDisassembler &o) const + bool operator<(const ShaderProcessingTool &o) const { if(tool != o.tool) return tool < o.tool; @@ -83,28 +138,61 @@ struct SPIRVDisassembler return executable < o.executable; if(args != o.args) return args < o.args; + if(input != o.input) + return input < o.input; + if(output != o.output) + return output < o.output; return false; } - DOCUMENT("The :class:`KnownSPIRVTool` identifying which known tool this disassembler is."); - KnownSPIRVTool tool = KnownSPIRVTool::Unknown; + DOCUMENT("The :class:`KnownShaderTool` identifying which known tool this program is."); + KnownShaderTool tool = KnownShaderTool::Unknown; DOCUMENT("The human-readable name of the program."); rdcstr name; DOCUMENT("The path to the executable to run for this program."); rdcstr executable; DOCUMENT("The command line argmuents to pass to the program."); rdcstr args; + DOCUMENT("The input that this program expects."); + ShaderEncoding input = ShaderEncoding::Unknown; + DOCUMENT("The output that this program provides."); + ShaderEncoding output = ShaderEncoding::Unknown; - DOCUMENT(R"(Runs this disassembler for a given shader reflection. + DOCUMENT(R"(Return the default arguments used when invoking this tool + +:return: The arguments specified for this tool. +:rtype: ``str`` +)"); + rdcstr DefaultArguments() const; + + DOCUMENT(R"(Runs this program to disassemble a given shader reflection. :param QWidget window: A handle to the window to use when showing a progress bar or error messages. :param ~renderdoc.ShaderReflection shader: The shader to disassemble. +:param str args: arguments to pass to the tool. The default arguments can be obtained using + :meth:`DefaultArguments` which can then be customised as desired. Passing an empty string uses the + default arguments. :return: The disassembly, or an empty string if something went wrong. :rtype: ``str`` )"); - rdcstr DisassembleShader(QWidget *window, const ShaderReflection *reflection) const; + rdcstr DisassembleShader(QWidget *window, const ShaderReflection *reflection, rdcstr args) const; + + DOCUMENT(R"(Runs this program to disassemble a given shader source. + +:param QWidget window: A handle to the window to use when showing a progress bar or error messages. +:param str source: The source code, preprocessed into a single file. +:param str entryPoint: The name of the entry point in the shader to compile. +:param ~renderdoc.ShaderStage stage: The pipeline stage that this shader represents. +:param str args: arguments to pass to the tool. The default arguments can be obtained using + :meth:`DefaultArguments` which can then be customised as desired. Passing an empty string uses the + default arguments. +:return: The compiled shader code, or an empty buffer if something went wrong. +:rtype: ``bytes`` +)"); + bytebuf CompileShader(QWidget *window, rdcstr source, rdcstr entryPoint, ShaderStage stage, + rdcstr args) const; }; -DECLARE_REFLECTION_STRUCT(SPIRVDisassembler); +DECLARE_REFLECTION_STRUCT(ShaderProcessingTool); #define BUGREPORT_URL "https://renderdoc.org/bugreporter" @@ -239,7 +327,7 @@ DECLARE_REFLECTION_STRUCT(BugReport); \ CONFIG_SETTING_VAL(public, bool, bool, AllowGlobalHook, false) \ \ - CONFIG_SETTING(public, QVariantList, rdcarray, SPIRVDisassemblers) \ + CONFIG_SETTING(public, QVariantList, rdcarray, ShaderProcessors) \ \ CONFIG_SETTING_VAL(public, bool, bool, Analytics_TotalOptOut, false) \ \ @@ -548,10 +636,10 @@ For more information about some of these settings that are user-facing see Defaults to ``False``. -.. data:: SPIRVDisassemblers +.. data:: ShaderProcessors - A list of :class:`SPIRVDisassembler` detailing the potential disassembler programs. The first one - in the list is the default. + A list of :class:`ShaderProcessingTool` detailing shader processing programs. The list comes in + priority order, with earlier processors preferred over later ones. .. data:: Analytics_TotalOptOut diff --git a/qrenderdoc/Code/Interface/SPIRVDisassembler.cpp b/qrenderdoc/Code/Interface/SPIRVDisassembler.cpp deleted file mode 100644 index ab2a8198f..000000000 --- a/qrenderdoc/Code/Interface/SPIRVDisassembler.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/****************************************************************************** - * The MIT License (MIT) - * - * Copyright (c) 2016-2018 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 -#include -#include "Code/QRDUtils.h" -#include "QRDInterface.h" - -template <> -std::string DoStringise(const KnownSPIRVTool &el) -{ - BEGIN_ENUM_STRINGISE(KnownSPIRVTool); - { - STRINGISE_ENUM_CLASS_NAMED(Unknown, "Custom Tool"); - STRINGISE_ENUM_CLASS_NAMED(SPIRV_Cross, "SPIRV-Cross"); - STRINGISE_ENUM_CLASS_NAMED(spirv_dis, "spirv-dis"); - } - END_ENUM_STRINGISE(); -} - -rdcstr SPIRVDisassembler::DisassembleShader(QWidget *window, const ShaderReflection *shaderDetails) const -{ - if(executable.isEmpty()) - return ""; - - QString spv_bin_file = QDir(QDir::tempPath()).absoluteFilePath(lit("spv_bin.spv")); - - QFile binHandle(spv_bin_file); - if(binHandle.open(QFile::WriteOnly | QIODevice::Truncate)) - { - binHandle.write( - QByteArray((const char *)shaderDetails->rawBytes.data(), shaderDetails->rawBytes.count())); - binHandle.close(); - } - else - { - RDDialog::critical( - window, QApplication::translate("SPIRVDisassembler", "Error writing temp file"), - QApplication::translate("SPIRVDisassembler", "Couldn't write temporary SPIR-V file %1.") - .arg(spv_bin_file)); - return ""; - } - - QString programArguments = args; - - switch(tool) - { - case KnownSPIRVTool::SPIRV_Cross: - programArguments = lit("--output {spv_disas} {spv_bin} --vulkan-semantics"); - break; - case KnownSPIRVTool::spirv_dis: - programArguments = lit("--no-color -o {spv_disas} {spv_bin}"); - break; - default: break; - } - - if(!programArguments.contains(lit("{spv_bin}"))) - { - RDDialog::critical( - window, QApplication::translate("SPIRVDisassembler", "Wrongly configured disassembler"), - QApplication::translate( - "SPIRVDisassembler", - "Please use {spv_bin} in the disassembler arguments to specify the input file.")); - return ""; - } - - QString glsl; - - LambdaThread *thread = new LambdaThread([this, window, &glsl, programArguments, spv_bin_file]() { - QString spv_disas_file = QDir(QDir::tempPath()).absoluteFilePath(lit("spv_disas.txt")); - - QString expandedargs = programArguments; - - bool writesToFile = expandedargs.contains(lit("{spv_disas}")); - - expandedargs.replace(lit("{spv_bin}"), spv_bin_file); - expandedargs.replace(lit("{spv_disas}"), spv_disas_file); - - QStringList argList = ParseArgsList(expandedargs); - - QProcess process; - process.start(executable, argList); - process.waitForFinished(); - - if(process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) - { - if(window) - { - GUIInvoke::call(window, [window]() { - RDDialog::critical( - window, QApplication::translate("SPIRVDisassembler", "Error running disassembler"), - QApplication::translate( - "SPIRVDisassembler", - "There was an error invoking the external SPIR-V disassembler.")); - }); - } - } - - if(writesToFile) - { - QFile outputHandle(spv_disas_file); - if(outputHandle.open(QFile::ReadOnly | QIODevice::Text)) - { - glsl = QString::fromUtf8(outputHandle.readAll()); - outputHandle.close(); - } - } - else - { - glsl = QString::fromUtf8(process.readAll()); - } - - QFile::remove(spv_bin_file); - QFile::remove(spv_disas_file); - }); - thread->start(); - - ShowProgressDialog(window, QApplication::translate("SPIRVDisassembler", - "Please wait - running external disassembler"), - [thread]() { return !thread->isRunning(); }); - - thread->deleteLater(); - - return glsl; -} diff --git a/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp b/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp new file mode 100644 index 000000000..2011783a8 --- /dev/null +++ b/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp @@ -0,0 +1,247 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 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 +#include +#include "Code/QRDUtils.h" +#include "QRDInterface.h" + +template <> +std::string DoStringise(const KnownShaderTool &el) +{ + BEGIN_ENUM_STRINGISE(KnownShaderTool); + { + STRINGISE_ENUM_CLASS_NAMED(Unknown, "Custom Tool"); + STRINGISE_ENUM_CLASS_NAMED(SPIRV_Cross, "SPIRV-Cross"); + STRINGISE_ENUM_CLASS_NAMED(spirv_dis, "spirv-dis"); + STRINGISE_ENUM_CLASS_NAMED(glslangValidatorGLSL, "glslang (GLSL)"); + STRINGISE_ENUM_CLASS_NAMED(glslangValidatorHLSL, "glslang (HLSL)"); + STRINGISE_ENUM_CLASS_NAMED(spirv_as, "spirv-as"); + } + END_ENUM_STRINGISE(); +} + +rdcstr ShaderProcessingTool::DisassembleShader(QWidget *window, const ShaderReflection *shaderDetails, + rdcstr arguments) const +{ + if(executable.isEmpty()) + return ""; + + QString input_file = QDir(QDir::tempPath()).absoluteFilePath(lit("shader_input")); + QString output_file = QDir(QDir::tempPath()).absoluteFilePath(lit("shader_output")); + + QFile binHandle(input_file); + if(binHandle.open(QFile::WriteOnly | QIODevice::Truncate)) + { + binHandle.write( + QByteArray((const char *)shaderDetails->rawBytes.data(), shaderDetails->rawBytes.count())); + binHandle.close(); + } + else + { + RDDialog::critical( + window, QApplication::translate("ShaderProcessingTool", "Error writing temp file"), + QApplication::translate("ShaderProcessingTool", "Couldn't write temporary file %1.") + .arg(input_file)); + return ""; + } + + QString programArguments = arguments; + + if(programArguments.isEmpty()) + programArguments = DefaultArguments(); + + if(!programArguments.contains(lit("{input_file}"))) + { + RDDialog::critical( + window, QApplication::translate("ShaderProcessingTool", "Wrongly configured tool"), + QApplication::translate( + "ShaderProcessingTool", + "Please use {input_file} in the tool arguments to specify the input file.")); + return ""; + } + + QString outputData; + + QString expandedargs = programArguments; + + bool writesToFile = expandedargs.contains(lit("{output_file}")); + + expandedargs.replace(lit("{input_file}"), input_file); + expandedargs.replace(lit("{output_file}"), output_file); + + QStringList argList = ParseArgsList(expandedargs); + + LambdaThread *thread = + new LambdaThread([this, window, &outputData, argList, input_file, output_file, writesToFile]() { + QProcess process; + process.start(executable, argList); + process.waitForFinished(); + + if(process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) + { + if(window) + { + GUIInvoke::call(window, [window]() { + RDDialog::critical( + window, QApplication::translate("ShaderProcessingTool", "Error running tool"), + QApplication::translate( + "ShaderProcessingTool", + "There was an error invoking the external shader processing tool.")); + }); + } + } + + if(writesToFile) + { + QFile outputHandle(output_file); + if(outputHandle.open(QFile::ReadOnly)) + { + outputData = QString::fromUtf8(outputHandle.readAll()); + outputHandle.close(); + } + } + else + { + outputData = QString::fromUtf8(process.readAll()); + } + + QFile::remove(input_file); + QFile::remove(output_file); + }); + thread->start(); + + ShowProgressDialog(window, QApplication::translate("ShaderProcessingTool", + "Please wait - running external tool"), + [thread]() { return !thread->isRunning(); }); + + thread->deleteLater(); + + return outputData; +} + +bytebuf ShaderProcessingTool::CompileShader(QWidget *window, rdcstr source, rdcstr entryPoint, + ShaderStage stage, rdcstr arguments) const +{ + if(executable.isEmpty()) + return ""; + + QString input_file = QDir(QDir::tempPath()).absoluteFilePath(lit("shader_input")); + QString output_file = QDir(QDir::tempPath()).absoluteFilePath(lit("shader_output")); + + QFile binHandle(input_file); + if(binHandle.open(QFile::WriteOnly | QIODevice::Truncate)) + { + binHandle.write(QByteArray((const char *)source.c_str(), source.count())); + binHandle.close(); + } + else + { + RDDialog::critical( + window, QApplication::translate("ShaderProcessingTool", "Error writing temp file"), + QApplication::translate("ShaderProcessingTool", "Couldn't write temporary file %1.") + .arg(input_file)); + return ""; + } + + QString programArguments = arguments; + + if(programArguments.isEmpty()) + programArguments = DefaultArguments(); + + if(!programArguments.contains(lit("{input_file}"))) + { + RDDialog::critical( + window, QApplication::translate("ShaderProcessingTool", "Wrongly configured tool"), + QApplication::translate( + "ShaderProcessingTool", + "Please use {input_file} in the tool arguments to specify the input file.")); + return ""; + } + + bytebuf outputData; + + QString expandedargs = programArguments; + + bool writesToFile = expandedargs.contains(lit("{output_file}")); + + expandedargs.replace(lit("{input_file}"), input_file); + expandedargs.replace(lit("{entry_point}"), entryPoint); + expandedargs.replace(lit("{output_file}"), output_file); + + const QString glsl_stage4[ENUM_ARRAY_SIZE(ShaderStage)] = { + lit("vert"), lit("tesc"), lit("tese"), lit("geom"), lit("frag"), lit("comp"), + }; + + expandedargs.replace(lit("{glsl_stage4}"), glsl_stage4[int(stage)]); + + QStringList argList = ParseArgsList(expandedargs); + + LambdaThread *thread = + new LambdaThread([this, window, &outputData, argList, input_file, output_file, writesToFile]() { + QProcess process; + process.start(executable, argList); + process.waitForFinished(); + + if(process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) + { + if(window) + { + GUIInvoke::call(window, [window]() { + RDDialog::critical( + window, QApplication::translate("ShaderProcessingTool", "Error running tool"), + QApplication::translate( + "ShaderProcessingTool", + "There was an error invoking the external shader processing tool.")); + }); + } + } + + if(writesToFile) + { + QFile outputHandle(output_file); + if(outputHandle.open(QFile::ReadOnly)) + { + outputData = outputHandle.readAll(); + outputHandle.close(); + } + } + else + { + outputData = process.readAll(); + } + + QFile::remove(input_file); + QFile::remove(output_file); + }); + thread->start(); + + ShowProgressDialog(window, QApplication::translate("ShaderProcessingTool", + "Please wait - running external tool"), + [thread]() { return !thread->isRunning(); }); + + thread->deleteLater(); + + return outputData; +} diff --git a/qrenderdoc/Code/pyrenderdoc/qrenderdoc.i b/qrenderdoc/Code/pyrenderdoc/qrenderdoc.i index 4aa9cffce..ca7125ee4 100644 --- a/qrenderdoc/Code/pyrenderdoc/qrenderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/qrenderdoc.i @@ -80,7 +80,7 @@ TEMPLATE_ARRAY_DECLARE(rdcarray); DOCUMENT(""); TEMPLATE_ARRAY_INSTANTIATE(rdcarray, EventBookmark) -TEMPLATE_ARRAY_INSTANTIATE(rdcarray, SPIRVDisassembler) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderProcessingTool) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, rdcstrpair) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, BugReport) TEMPLATE_ARRAY_INSTANTIATE_PTR(rdcarray, ICaptureViewer) diff --git a/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp b/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp index 5cb5d206d..633dabfd8 100644 --- a/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp +++ b/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp @@ -69,14 +69,26 @@ extern "C" PyObject *QWidgetToPy(QWidget *widget) } //////////////////////////////////////////////////////////////////////////////// -// SPIRVDisassembler.cpp stubs +// ShaderProcessingTool.cpp stubs //////////////////////////////////////////////////////////////////////////////// -rdcstr SPIRVDisassembler::DisassembleShader(QWidget *window, const ShaderReflection *shaderDetails) const +rdcstr ShaderProcessingTool::DefaultArguments() const { return ""; } +rdcstr ShaderProcessingTool::DisassembleShader(QWidget *window, const ShaderReflection *shaderDetails, + rdcstr arguments) const +{ + return ""; +} + +bytebuf ShaderProcessingTool::CompileShader(QWidget *window, rdcstr source, rdcstr entryPoint, + ShaderStage stage, rdcstr arguments) const +{ + return bytebuf(); +} + //////////////////////////////////////////////////////////////////////////////// // PersistantConfig.cpp stubs //////////////////////////////////////////////////////////////////////////////// diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 31e8121c7..e6c54d211 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -24,6 +24,8 @@ #include "SettingsDialog.h" #include +#include +#include #include "Code/Interface/QRDInterface.h" #include "Code/QRDUtils.h" #include "Styles/StyleData.h" @@ -31,61 +33,6 @@ #include "CaptureDialog.h" #include "ui_SettingsDialog.h" -class KnownSPIRVToolDelegate : public QStyledItemDelegate -{ -public: - explicit KnownSPIRVToolDelegate(QWidget *parent = NULL) : QStyledItemDelegate(parent) {} - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const override - { - QComboBox *editor = new QComboBox(parent); - - editor->setEditable(true); - editor->setInsertPolicy(QComboBox::NoInsert); - - QStringList items; - for(KnownSPIRVTool tool : values()) - items << ToQStr(tool); - editor->addItems(items); - - return editor; - } - - void setEditorData(QWidget *editor, const QModelIndex &index) const override - { - QComboBox *comboEditor = qobject_cast(editor); - if(comboEditor) - { - QString editData = index.data(Qt::EditRole).toString(); - - int idx = comboEditor->findText(editData); - - if(idx >= 0) - comboEditor->setCurrentIndex(idx); - else - comboEditor->setCurrentText(index.data(Qt::EditRole).toString()); - - return; - } - - QStyledItemDelegate::setEditorData(editor, index); - } - - void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override - { - QComboBox *comboEditor = qobject_cast(editor); - if(comboEditor) - { - model->setData(index, comboEditor->currentText(), Qt::EditRole); - return; - } - - QStyledItemDelegate::setModelData(editor, model, index); - } - -private slots: -}; - SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) : QDialog(parent), ui(new Ui::SettingsDialog), m_Ctx(ctx) { @@ -135,25 +82,22 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) ui->saveDirectory->setText(m_Ctx.Config().DefaultCaptureSaveDirectory); ui->tempDirectory->setText(m_Ctx.Config().TemporaryCaptureDirectory); - ui->disassemblers->setColumnCount(3); - ui->disassemblers->setHorizontalHeaderLabels(QStringList() << tr("Tool") << tr("Executable") - << tr("Arguments")); + ui->shaderTools->setColumnCount(2); + ui->shaderTools->setHorizontalHeaderLabels(QStringList() << tr("Tool") << tr("Process")); - ui->disassemblers->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive); - ui->disassemblers->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive); - ui->disassemblers->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); + ui->shaderTools->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive); + ui->shaderTools->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); - for(const SPIRVDisassembler &disasm : m_Ctx.Config().SPIRVDisassemblers) - addDisassembler(disasm); + for(const ShaderProcessingTool &tool : m_Ctx.Config().ShaderProcessors) + addProcessor(tool); - ui->disassemblers->horizontalHeader()->resizeSection(0, 100); + ui->shaderTools->horizontalHeader()->resizeSection(0, 100); - ui->disassemblers->verticalHeader()->setSectionsMovable(true); - ui->disassemblers->verticalHeader()->setMinimumWidth(20); + ui->shaderTools->verticalHeader()->setSectionsMovable(true); + ui->shaderTools->verticalHeader()->setMinimumWidth(20); - ui->disassemblers->setItemDelegateForColumn(0, new KnownSPIRVToolDelegate(this)); - - ui->deleteDisasm->setEnabled(false); + ui->deleteShaderTool->setEnabled(false); + ui->editShaderTool->setEnabled(false); ui->ExternalTool_RadeonGPUProfiler->setText(m_Ctx.Config().ExternalTool_RadeonGPUProfiler); @@ -230,8 +174,8 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) m_Init = false; - QObject::connect(ui->disassemblers->verticalHeader(), &QHeaderView::sectionMoved, this, - &SettingsDialog::disassemblers_rowMoved); + QObject::connect(ui->shaderTools->verticalHeader(), &QHeaderView::sectionMoved, this, + &SettingsDialog::shaderTools_rowMoved); QObject::connect(ui->Formatter_MinFigures, OverloadedSlot::of(&QSpinBox::valueChanged), this, &SettingsDialog::formatter_valueChanged); QObject::connect(ui->Formatter_MaxFigures, OverloadedSlot::of(&QSpinBox::valueChanged), this, @@ -488,144 +432,349 @@ void SettingsDialog::on_ShaderViewer_FriendlyNaming_toggled(bool checked) m_Ctx.Config().Save(); } -void SettingsDialog::addDisassembler(const SPIRVDisassembler &disasm) +void SettingsDialog::addProcessor(const ShaderProcessingTool &tool) { - // prevent calling cellChanged - m_AddingDisassembler = true; + int row = ui->shaderTools->rowCount(); + ui->shaderTools->insertRow(row); - int row = ui->disassemblers->rowCount(); - ui->disassemblers->insertRow(row); + ui->shaderTools->setVerticalHeaderItem(row, new QTableWidgetItem(QString())); - ui->disassemblers->setVerticalHeaderItem(row, new QTableWidgetItem(QString())); + ui->shaderTools->setItem(row, 0, new QTableWidgetItem(tool.name)); + ui->shaderTools->setItem( + row, 1, + new QTableWidgetItem(QFormatStr("%1 -> %2").arg(ToQStr(tool.input)).arg(ToQStr(tool.output)))); +} - ui->disassemblers->setItem(row, 0, new QTableWidgetItem(disasm.name)); - ui->disassemblers->setItem(row, 1, new QTableWidgetItem(disasm.executable)); +bool SettingsDialog::editTool(int existing, ShaderProcessingTool &tool) +{ + QDialog dialog; + dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); - QTableWidgetItem *item = new QTableWidgetItem( - disasm.tool == KnownSPIRVTool::Unknown ? QString(disasm.args) : tr("Automatic")); - ui->disassemblers->setItem(row, 2, item); + dialog.resize(400, 0); - // make arguments non-editable for built-in tools - if(disasm.tool != KnownSPIRVTool::Unknown) + QGridLayout grid(&dialog); + + QLabel *lab; + + lab = new QLabel(tr("Name:"), &dialog); + lab->setAlignment(Qt::AlignRight | Qt::AlignTop); + grid.addWidget(lab, 0, 0, 1, 1); + + lab = new QLabel(tr("Tool Type:"), &dialog); + lab->setAlignment(Qt::AlignRight | Qt::AlignTop); + grid.addWidget(lab, 1, 0, 1, 1); + + lab = new QLabel(tr("Executable:"), &dialog); + lab->setAlignment(Qt::AlignRight | Qt::AlignTop); + grid.addWidget(lab, 2, 0, 1, 1); + + lab = new QLabel(tr("Command Line:"), &dialog); + lab->setAlignment(Qt::AlignRight | Qt::AlignTop); + grid.addWidget(lab, 3, 0, 1, 1); + + lab = new QLabel(tr("Input/Output:"), &dialog); + lab->setAlignment(Qt::AlignRight | Qt::AlignTop); + grid.addWidget(lab, 4, 0, 1, 1); + + QLineEdit nameEdit; + nameEdit.setPlaceholderText(lit("Tool Name")); + nameEdit.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + nameEdit.setMinimumHeight(20); + + QStringList strs; + + for(KnownShaderTool t : values()) { - Qt::ItemFlags flags = item->flags() & ~Qt::ItemIsEditable; - item->setFlags(flags); + if(t == KnownShaderTool::Unknown) + strs << tr("Custom Tool"); + else + strs << ToQStr(t); } - m_AddingDisassembler = false; -} + QComboBox toolEdit; + toolEdit.addItems(strs); + toolEdit.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); -void SettingsDialog::on_addDisasm_clicked() -{ - SPIRVDisassembler disasm; - disasm.name = tr("Custom Tool"); - disasm.executable = lit("path/to/executable"); - disasm.args = lit("--input {spv_bin} --output {spv_disasm}"); - m_Ctx.Config().SPIRVDisassemblers.push_back(disasm); + QHBoxLayout executableLayout; - addDisassembler(disasm); + QLineEdit executableEdit; + executableEdit.setPlaceholderText(lit("tool")); + executableEdit.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + executableEdit.setMinimumHeight(20); + QToolButton executableBrowse; + executableBrowse.setText(lit("...")); + executableBrowse.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - m_Ctx.Config().Save(); -} + executableLayout.addWidget(&executableEdit); + executableLayout.addWidget(&executableBrowse); -void SettingsDialog::on_deleteDisasm_clicked() -{ - int row = -1; + QTextEdit argsEdit; + argsEdit.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + argsEdit.setMinimumHeight(80); - QModelIndexList selected = ui->disassemblers->selectionModel()->selectedRows(); + strs.clear(); - if(!selected.isEmpty()) - row = selected[0].row(); - - if(row < 0 || row >= m_Ctx.Config().SPIRVDisassemblers.count()) - return; - - const SPIRVDisassembler &disasm = m_Ctx.Config().SPIRVDisassemblers[row]; - - QMessageBox::StandardButton res = RDDialog::question( - this, tr("Are you sure?"), tr("Are you sure you want to delete '%1'?").arg(disasm.name), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); - - if(res == QMessageBox::Yes) + for(ShaderEncoding enc : values()) { - ui->disassemblers->removeRow(row); - m_Ctx.Config().SPIRVDisassemblers.erase(row); + if(enc == ShaderEncoding::Unknown) + continue; + else + strs << ToQStr(enc); + } + + QHBoxLayout inputOutputLayout; + + QComboBox inputEdit; + inputEdit.addItems(strs); + + QComboBox outputEdit; + outputEdit.addItems(strs); + + inputOutputLayout.addWidget(&inputEdit); + inputOutputLayout.addWidget(&outputEdit); + + grid.addWidget(&nameEdit, 0, 1, 1, 1); + grid.addWidget(&toolEdit, 1, 1, 1, 1); + grid.addLayout(&executableLayout, 2, 1, 1, 1); + grid.addWidget(&argsEdit, 3, 1, 1, 1); + grid.addLayout(&inputOutputLayout, 4, 1, 1, 1); + + QDialogButtonBox buttons; + buttons.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + grid.addWidget(&buttons, 5, 0, 1, 2); + + QObject::connect(&buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + + QObject::connect(&executableBrowse, &QToolButton::clicked, [&]() { + QString initDir; + + QFileInfo f(executableEdit.text()); + QDir dir = f.dir(); + if(f.isAbsolute() && dir.exists()) + { + initDir = dir.absolutePath(); + } + + QString filename = RDDialog::getExecutableFileName(this, tr("Choose executable"), initDir); + + if(!filename.isEmpty()) + executableEdit.setText(filename); + }); + + QObject::connect(&toolEdit, OverloadedSlot::of(&QComboBox::currentIndexChanged), + [&](int index) { + if(index > 0) + { + KnownShaderTool tool = KnownShaderTool(index); + + // -1 because we skip ShaderEncoding::Unknown + inputEdit.setCurrentIndex(int(ToolInput(tool)) - 1); + outputEdit.setCurrentIndex(int(ToolOutput(tool)) - 1); + argsEdit.setEnabled(false); + inputEdit.setEnabled(false); + outputEdit.setEnabled(false); + } + else + { + argsEdit.setEnabled(true); + inputEdit.setEnabled(true); + outputEdit.setEnabled(true); + } + }); + + // -1 because we skip ShaderEncoding::Unknown + inputEdit.setCurrentIndex(int(tool.input) - 1); + outputEdit.setCurrentIndex(int(tool.output) - 1); + executableEdit.setText(tool.executable); + argsEdit.setText(tool.args); + nameEdit.setText(tool.name); + toolEdit.setCurrentIndex(int(tool.tool)); + + bool invalid = false; + + do + { + RDDialog::show(&dialog); + + // don't validate if they cancelled + if(dialog.result() != QDialog::Accepted) + return false; + + tool.tool = KnownShaderTool(toolEdit.currentIndex()); + tool.name = nameEdit.text(); + tool.executable = executableEdit.text(); + tool.args = argsEdit.toPlainText(); + // +1 because we skip ShaderEncoding::Unknown + tool.input = ShaderEncoding(inputEdit.currentIndex() + 1); + tool.output = ShaderEncoding(outputEdit.currentIndex() + 1); + + QString message; + + // ensure we don't have an invalid name + if(tool.name == "Builtin") + { + invalid = true; + message = tr("'Builtin' is a reserved tool name, please select another."); + } + else if(tool.name.isEmpty()) + { + invalid = true; + message = tr("No tool name specified."); + } + else if(tool.executable.isEmpty()) + { + invalid = true; + message = tr("No tool executable selected."); + } + else if(tool.input == ShaderEncoding::Unknown) + { + invalid = true; + message = tr("Input type cannot be unknown."); + } + else if(tool.output == ShaderEncoding::Unknown) + { + invalid = true; + message = tr("Output type cannot be unknown."); + } + else if(tool.tool == KnownShaderTool::Unknown && + !QString(tool.args).contains(lit("{input_file}"))) + { + invalid = true; + message = tr("Custom tool arguments must include at least {input_file}."); + } + else + { + for(int i = 0; i < m_Ctx.Config().ShaderProcessors.count(); i++) + { + if(i == existing) + continue; + + if(tool.name == m_Ctx.Config().ShaderProcessors[i].name) + { + invalid = true; + message = tr("There's already a tool named '%1', please select another.").arg(tool.name); + break; + } + } + } + + if(invalid) + { + RDDialog::critical(this, tr("Invalid parameters specified"), message); + } + } while(invalid); + + return true; +} + +void SettingsDialog::on_addShaderTool_clicked() +{ + ShaderProcessingTool tool; + // start with example arguments + tool.args = lit("--input {input_file} --output {output_file} --mode foo"); + // impossible to pick a single default, but at least show the principle. + tool.input = ShaderEncoding::HLSL; + tool.output = ShaderEncoding::SPIRV; + + bool success = editTool(-1, tool); + + if(success) + { + m_Ctx.Config().ShaderProcessors.push_back(tool); + + addProcessor(tool); m_Ctx.Config().Save(); } } -void SettingsDialog::on_disassemblers_itemSelectionChanged() +void SettingsDialog::on_editShaderTool_clicked() { - ui->deleteDisasm->setEnabled(!ui->disassemblers->selectionModel()->selectedIndexes().empty()); -} + int row = -1; -void SettingsDialog::on_disassemblers_cellChanged(int row, int column) -{ - if(m_AddingDisassembler || row < 0 || row >= m_Ctx.Config().SPIRVDisassemblers.count()) + QModelIndexList selected = ui->shaderTools->selectionModel()->selectedRows(); + + if(!selected.isEmpty()) + row = selected[0].row(); + + if(row < 0 || row >= m_Ctx.Config().ShaderProcessors.count()) return; - SPIRVDisassembler &disasm = m_Ctx.Config().SPIRVDisassemblers[row]; + ShaderProcessingTool tool = m_Ctx.Config().ShaderProcessors[row]; - QString cellData = ui->disassemblers->item(row, column)->text(); + bool success = editTool(row, tool); - if(column == 0) + if(success) { - bool found = false; - - for(KnownSPIRVTool tool : values()) - { - if(ToQStr(tool) == cellData) - { - disasm.tool = tool; - disasm.name = cellData; - found = true; - - // make arguments non-editable - Qt::ItemFlags flags = ui->disassemblers->item(row, 2)->flags() & ~Qt::ItemIsEditable; - ui->disassemblers->item(row, 2)->setFlags(flags); - } - } - - if(!found) - { - disasm.tool = KnownSPIRVTool::Unknown; - disasm.name = cellData; - - // make arguments editable - Qt::ItemFlags flags = ui->disassemblers->item(row, 2)->flags() | Qt::ItemIsEditable; - ui->disassemblers->item(row, 2)->setFlags(flags); - } + ui->shaderTools->setItem(row, 0, new QTableWidgetItem(tool.name)); + ui->shaderTools->setItem( + row, 1, new QTableWidgetItem( + QFormatStr("%1 -> %2").arg(ToQStr(tool.input)).arg(ToQStr(tool.output)))); + m_Ctx.Config().ShaderProcessors[row] = tool; + m_Ctx.Config().Save(); } - else if(column == 1) - { - disasm.executable = cellData; - } - else if(column == 2) - { - disasm.args = cellData; - } - - m_Ctx.Config().Save(); } -void SettingsDialog::on_disassemblers_keyPress(QKeyEvent *event) +void SettingsDialog::on_deleteShaderTool_clicked() +{ + int row = -1; + + QModelIndexList selected = ui->shaderTools->selectionModel()->selectedRows(); + + if(!selected.isEmpty()) + row = selected[0].row(); + + if(row < 0 || row >= m_Ctx.Config().ShaderProcessors.count()) + return; + + const ShaderProcessingTool &tool = m_Ctx.Config().ShaderProcessors[row]; + + QMessageBox::StandardButton res = RDDialog::question( + this, tr("Are you sure?"), tr("Are you sure you want to delete '%1'?").arg(tool.name), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + + if(res == QMessageBox::Yes) + { + ui->shaderTools->removeRow(row); + m_Ctx.Config().ShaderProcessors.erase(row); + + m_Ctx.Config().Save(); + } +} + +void SettingsDialog::on_shaderTools_itemSelectionChanged() +{ + ui->deleteShaderTool->setEnabled(!ui->shaderTools->selectionModel()->selectedIndexes().empty()); + ui->editShaderTool->setEnabled(ui->deleteShaderTool->isEnabled()); +} + +void SettingsDialog::on_shaderTools_keyPress(QKeyEvent *event) { if(event->key() == Qt::Key_Delete) { - ui->deleteDisasm->click(); + ui->deleteShaderTool->click(); + } + if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) + { + ui->editShaderTool->click(); } } -void SettingsDialog::disassemblers_rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +void SettingsDialog::on_shaderTools_itemDoubleClicked(QTableWidgetItem *item) { - if(oldVisualIndex < 0 || oldVisualIndex >= m_Ctx.Config().SPIRVDisassemblers.count() || - newVisualIndex < 0 || newVisualIndex >= m_Ctx.Config().SPIRVDisassemblers.count()) + ui->editShaderTool->click(); +} + +void SettingsDialog::shaderTools_rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +{ + if(oldVisualIndex < 0 || oldVisualIndex >= m_Ctx.Config().ShaderProcessors.count() || + newVisualIndex < 0 || newVisualIndex >= m_Ctx.Config().ShaderProcessors.count()) return; - SPIRVDisassembler disasm = m_Ctx.Config().SPIRVDisassemblers.at(oldVisualIndex); - m_Ctx.Config().SPIRVDisassemblers.erase(oldVisualIndex); - m_Ctx.Config().SPIRVDisassemblers.insert(newVisualIndex, disasm); + ShaderProcessingTool tool = m_Ctx.Config().ShaderProcessors.at(oldVisualIndex); + m_Ctx.Config().ShaderProcessors.erase(oldVisualIndex); + m_Ctx.Config().ShaderProcessors.insert(newVisualIndex, tool); m_Ctx.Config().Save(); } @@ -801,4 +950,4 @@ void SettingsDialog::on_UIStyle_currentIndexChanged(int index) } m_Ctx.Config().Save(); -} +} \ No newline at end of file diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.h b/qrenderdoc/Windows/Dialogs/SettingsDialog.h index 04f0821ff..b59b57ac5 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.h +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.h @@ -31,8 +31,9 @@ namespace Ui class SettingsDialog; } +class QTableWidgetItem; class QListWidgetItem; -struct SPIRVDisassembler; +struct ShaderProcessingTool; struct ICaptureContext; @@ -78,12 +79,13 @@ private slots: // shader viewer void on_ShaderViewer_FriendlyNaming_toggled(bool checked); - void on_addDisasm_clicked(); - void on_deleteDisasm_clicked(); - void on_disassemblers_itemSelectionChanged(); - void on_disassemblers_cellChanged(int row, int column); - void on_disassemblers_keyPress(QKeyEvent *event); - void disassemblers_rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); + void on_addShaderTool_clicked(); + void on_editShaderTool_clicked(); + void on_deleteShaderTool_clicked(); + void on_shaderTools_itemSelectionChanged(); + void on_shaderTools_keyPress(QKeyEvent *event); + void on_shaderTools_itemDoubleClicked(QTableWidgetItem *item); + void shaderTools_rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); // event browser void on_EventBrowser_TimeUnit_currentIndexChanged(int index); @@ -112,9 +114,9 @@ private slots: private: Ui::SettingsDialog *ui; - void addDisassembler(const SPIRVDisassembler &disasm); + void addProcessor(const ShaderProcessingTool &disasm); + bool editTool(int existing, ShaderProcessingTool &disasm); ICaptureContext &m_Ctx; bool m_Init = false; - bool m_AddingDisassembler = false; }; diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui index e507eac0f..bfa84f795 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui @@ -73,7 +73,7 @@ QTabWidget::West - 0 + 3 true @@ -129,6 +129,12 @@ E.g. a value of 3 means 0.005 / 10 = 5E-4 Qt::Vertical + + + 0 + 0 + + @@ -699,16 +705,9 @@ e.g. a value of 5 means 0.123456789 will display as 0.12345 - Vulkan Disassemblers + Shader Processing Tools - - - - <html><head/><body><p>Available SPIR-V Disassemblers. Click items to edit, drag row header to change priority.</p></body></html> - - - @@ -723,9 +722,9 @@ e.g. a value of 5 means 0.123456789 will display as 0.12345 - + - QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + QAbstractItemView::NoEditTriggers true @@ -766,14 +765,21 @@ e.g. a value of 5 means 0.123456789 will display as 0.12345 - + Add - + + + Edit + + + + + Delete @@ -781,6 +787,16 @@ e.g. a value of 5 means 0.123456789 will display as 0.12345 + + + + <html><head/><body><p>Available Shader processing tools. These tools can be configured to translate from one form to another. Typically to e.g. disassemble from SPIR-V to GLSL or canonical assembly, or to compile from HLSL back to SPIR-V or DXBC.</p><p>Double click items to edit, drag row header to change relative priority.</p></body></html> + + + true + + + diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index c7d1150c1..762109a9a 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -146,7 +146,7 @@ D3D11PipelineStateViewer::D3D11PipelineStateViewer(ICaptureContext &ctx, } for(QToolButton *b : editButtons) - QObject::connect(b, &QToolButton::clicked, this, &D3D11PipelineStateViewer::shaderEdit_clicked); + QObject::connect(b, &QToolButton::clicked, &m_Common, &PipelineStateViewer::shaderEdit_clicked); for(QToolButton *b : saveButtons) QObject::connect(b, &QToolButton::clicked, this, &D3D11PipelineStateViewer::shaderSave_clicked); @@ -1449,7 +1449,9 @@ void D3D11PipelineStateViewer::setState() if(stage == NULL || stage->resourceId == ResourceId()) continue; - b->setEnabled(stage->reflection); + b->setEnabled(stage->reflection != NULL); + + m_Common.SetupShaderEditButton(b, ResourceId(), stage->resourceId, stage->reflection); } vs = ui->csUAVs->verticalScrollBar()->value(); @@ -2309,40 +2311,6 @@ void D3D11PipelineStateViewer::shaderView_clicked() m_Ctx.AddDockWindow(shad->Widget(), DockReference::AddTo, this); } -void D3D11PipelineStateViewer::shaderEdit_clicked() -{ - QWidget *sender = qobject_cast(QObject::sender()); - const D3D11Pipe::Shader *stage = stageForSender(sender); - - if(!stage || stage->resourceId == ResourceId()) - return; - - const ShaderReflection *shaderDetails = stage->reflection; - - if(!shaderDetails) - return; - - QString entryFunc = lit("EditedShader%1S").arg(ToQStr(stage->stage, GraphicsAPI::D3D11)[0]); - - rdcstrpairs files; - - bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files); - - if(!hasOrigSource) - { - files.clear(); - files.push_back(make_rdcpair( - "generated.hlsl", m_Common.GenerateHLSLStub(shaderDetails, entryFunc))); - } - - if(files.empty()) - return; - - // we always consider the input HLSL, either the stub or the original source - m_Common.EditShader(stage->stage, stage->resourceId, shaderDetails, entryFunc, - ShaderEncoding::HLSL, files); -} - void D3D11PipelineStateViewer::shaderSave_clicked() { const D3D11Pipe::Shader *stage = stageForSender(qobject_cast(QObject::sender())); diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h index 4e00c3614..f1e496610 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h @@ -68,7 +68,6 @@ private slots: // manual slots void shaderView_clicked(); - void shaderEdit_clicked(); void shaderSave_clicked(); void resource_itemActivated(RDTreeWidgetItem *item, int column); diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui index 36a2f2250..6c7fafe95 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui @@ -645,6 +645,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -938,6 +941,12 @@ QSizePolicy::MinimumExpanding + + + 0 + 0 + + @@ -1026,6 +1035,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1316,6 +1328,12 @@ QSizePolicy::MinimumExpanding + + + 0 + 0 + + @@ -1404,6 +1422,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1694,6 +1715,12 @@ QSizePolicy::MinimumExpanding + + + 0 + 0 + + @@ -1782,6 +1809,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -2124,6 +2154,12 @@ QSizePolicy::MinimumExpanding + + + 0 + 0 + + @@ -2831,6 +2867,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -3121,6 +3160,12 @@ QSizePolicy::MinimumExpanding + + + 0 + 0 + + @@ -3989,6 +4034,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -4407,6 +4455,12 @@ QSizePolicy::MinimumExpanding + + + 0 + 0 + + @@ -4420,16 +4474,16 @@ - - RDTreeWidget - QTreeView -
Widgets/Extended/RDTreeWidget.h
-
RDLabel QLabel
Widgets/Extended/RDLabel.h
+ + RDTreeWidget + QTreeView +
Widgets/Extended/RDTreeWidget.h
+
PipelineFlowChart QFrame diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index 55d22ab93..5912d6ed5 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -184,7 +184,7 @@ D3D12PipelineStateViewer::D3D12PipelineStateViewer(ICaptureContext &ctx, } for(QToolButton *b : editButtons) - QObject::connect(b, &QToolButton::clicked, this, &D3D12PipelineStateViewer::shaderEdit_clicked); + QObject::connect(b, &QToolButton::clicked, &m_Common, &PipelineStateViewer::shaderEdit_clicked); for(QToolButton *b : saveButtons) QObject::connect(b, &QToolButton::clicked, this, &D3D12PipelineStateViewer::shaderSave_clicked); @@ -1468,6 +1468,8 @@ void D3D12PipelineStateViewer::setState() continue; b->setEnabled(stage->reflection && state.pipelineResourceId != ResourceId()); + + m_Common.SetupShaderEditButton(b, state.pipelineResourceId, stage->resourceId, stage->reflection); } bool streamoutSet = false; @@ -2137,40 +2139,6 @@ void D3D12PipelineStateViewer::shaderView_clicked() m_Ctx.AddDockWindow(shad->Widget(), DockReference::AddTo, this); } -void D3D12PipelineStateViewer::shaderEdit_clicked() -{ - QWidget *sender = qobject_cast(QObject::sender()); - const D3D12Pipe::Shader *stage = stageForSender(sender); - - if(!stage || stage->resourceId == ResourceId()) - return; - - const ShaderReflection *shaderDetails = stage->reflection; - - if(!shaderDetails) - return; - - QString entryFunc = lit("EditedShader%1S").arg(ToQStr(stage->stage, GraphicsAPI::D3D12)[0]); - - rdcstrpairs files; - - bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files); - - if(!hasOrigSource) - { - files.clear(); - files.push_back(make_rdcpair( - "generated.hlsl", m_Common.GenerateHLSLStub(shaderDetails, entryFunc))); - } - - if(files.empty()) - return; - - // we always consider the input HLSL, either the stub or the original source - m_Common.EditShader(stage->stage, stage->resourceId, shaderDetails, entryFunc, - ShaderEncoding::HLSL, files); -} - void D3D12PipelineStateViewer::shaderSave_clicked() { const D3D12Pipe::Shader *stage = stageForSender(qobject_cast(QObject::sender())); diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.h index f738da7cb..861b1ef21 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.h @@ -69,7 +69,6 @@ private slots: // manual slots void shaderView_clicked(); - void shaderEdit_clicked(); void shaderSave_clicked(); void resource_itemActivated(RDTreeWidgetItem *item, int column); void cbuffer_itemActivated(RDTreeWidgetItem *item, int column); diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui index 9455e2460..7c3b83dc2 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui @@ -549,6 +549,9 @@ :/page_white_edit.png:/page_white_edit.png + + + QToolButton::MenuButtonPopup Qt::ToolButtonTextBesideIcon @@ -941,6 +944,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1326,6 +1332,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1711,6 +1720,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -2695,6 +2707,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -3748,6 +3763,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp index 6002a741a..245c5c0ab 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp @@ -142,7 +142,7 @@ GLPipelineStateViewer::GLPipelineStateViewer(ICaptureContext &ctx, PipelineState } for(QToolButton *b : editButtons) - QObject::connect(b, &QToolButton::clicked, this, &GLPipelineStateViewer::shaderEdit_clicked); + QObject::connect(b, &QToolButton::clicked, &m_Common, &PipelineStateViewer::shaderEdit_clicked); for(QToolButton *b : saveButtons) QObject::connect(b, &QToolButton::clicked, this, &GLPipelineStateViewer::shaderSave_clicked); @@ -1384,7 +1384,9 @@ void GLPipelineStateViewer::setState() ShaderReflection *shaderDetails = stage->reflection; - b->setEnabled(shaderDetails); + b->setEnabled(shaderDetails != NULL); + + m_Common.SetupShaderEditButton(b, ResourceId(), stage->shaderResourceId, shaderDetails); } vs = ui->xfbBuffers->verticalScrollBar()->value(); @@ -2301,40 +2303,6 @@ void GLPipelineStateViewer::shaderView_clicked() m_Ctx.AddDockWindow(shad->Widget(), DockReference::AddTo, this); } -void GLPipelineStateViewer::shaderEdit_clicked() -{ - QWidget *sender = qobject_cast(QObject::sender()); - const GLPipe::Shader *stage = stageForSender(sender); - - if(!stage || stage->shaderResourceId == ResourceId()) - return; - - const ShaderReflection *shaderDetails = stage->reflection; - - if(!shaderDetails) - return; - - QString entryFunc = lit("EditedShader%1S").arg(ToQStr(stage->stage, GraphicsAPI::OpenGL)[0]); - - rdcstrpairs files; - - bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files); - - if(!hasOrigSource) - { - // this would only happen if the GL program is uploading SPIR-V instead of GLSL. - files.clear(); - files.push_back(make_rdcpair("generated.glsl", "// TODO - disassemble SPIR-V")); - } - - if(files.empty()) - return; - - // we always consider the input GLSL - m_Common.EditShader(stage->stage, stage->shaderResourceId, shaderDetails, entryFunc, - ShaderEncoding::GLSL, files); -} - void GLPipelineStateViewer::shaderSave_clicked() { const GLPipe::Shader *stage = stageForSender(qobject_cast(QObject::sender())); diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h index 98e450cf3..89cbdfd23 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h @@ -68,7 +68,6 @@ private slots: // manual slots void shaderView_clicked(); - void shaderEdit_clicked(); void shaderSave_clicked(); void resource_itemActivated(RDTreeWidgetItem *item, int column); void ubo_itemActivated(RDTreeWidgetItem *item, int column); diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui index 13baef50e..c9a38bdb5 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui @@ -558,6 +558,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1003,6 +1006,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1448,6 +1454,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1893,6 +1902,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -3316,6 +3328,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -4268,6 +4283,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index f20bf6245..3a88456b6 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -23,9 +23,11 @@ ******************************************************************************/ #include "PipelineStateViewer.h" +#include #include #include #include +#include #include #include "3rdparty/toolwindowmanager/ToolWindowManager.h" #include "Code/QRDUtils.h" @@ -49,6 +51,9 @@ PipelineStateViewer::PipelineStateViewer(ICaptureContext &ctx, QWidget *parent) m_Current = NULL; + for(size_t i = 0; i < ARRAY_COUNT(editMenus); i++) + editMenus[i] = new QMenu(this); + setToD3D11(); m_Ctx.AddCaptureViewer(this); @@ -613,7 +618,7 @@ void PipelineStateViewer::MakeShaderVariablesHLSL(bool cbufferContents, QString PipelineStateViewer::GenerateHLSLStub(const ShaderReflection *shaderDetails, const QString &entryFunc) { - QString hlsl = lit("// No HLSL available - function stub generated\n\n"); + QString hlsl = lit("// HLSL function stub generated\n\n"); const QString textureDim[ENUM_ARRAY_SIZE(TextureType)] = { lit("Unknown"), lit("Buffer"), lit("Texture1D"), lit("Texture1DArray"), @@ -718,19 +723,27 @@ QString PipelineStateViewer::GenerateHLSLStub(const ShaderReflection *shaderDeta return hlsl; } -void PipelineStateViewer::EditShader(ShaderStage shaderType, ResourceId id, - const ShaderReflection *shaderDetails, const QString &entry, - ShaderEncoding encoding, const rdcstrpairs &files) +void PipelineStateViewer::shaderEdit_clicked() { - if(!shaderDetails) + QToolButton *sender = qobject_cast(QObject::sender()); + if(!sender) return; + // activate the first item in the menu, if there are any items, as the default action. + QMenu *menu = sender->menu(); + if(menu && !menu->actions().isEmpty()) + menu->actions()[0]->trigger(); +} + +void PipelineStateViewer::EditShader(ResourceId id, ShaderStage shaderType, const rdcstr &entry, + ShaderCompileFlags compileFlags, ShaderEncoding encoding, + const rdcstrpairs &files) +{ IShaderViewer *sv = m_Ctx.EditShader( - false, shaderDetails->stage, entry, files, encoding, shaderDetails->debugInfo.compileFlags, + false, shaderType, entry, files, encoding, compileFlags, // save callback - [shaderType, id, shaderDetails](ICaptureContext *ctx, IShaderViewer *viewer, - ShaderEncoding shaderEncoding, ShaderCompileFlags flags, - rdcstr entryFunc, bytebuf shaderBytes) { + [shaderType, id](ICaptureContext *ctx, IShaderViewer *viewer, ShaderEncoding shaderEncoding, + ShaderCompileFlags flags, rdcstr entryFunc, bytebuf shaderBytes) { if(shaderBytes.isEmpty()) return; @@ -740,7 +753,7 @@ void PipelineStateViewer::EditShader(ShaderStage shaderType, ResourceId id, // invoke off to the ReplayController to replace the capture's shader // with our edited one ctx->Replay().AsyncInvoke([ctx, entryFunc, shaderBytes, shaderEncoding, flags, shaderType, - id, shaderDetails, viewer](IReplayController *r) { + id, viewer](IReplayController *r) { rdcstr errs; ResourceId from = id; @@ -776,6 +789,151 @@ void PipelineStateViewer::EditShader(ShaderStage shaderType, ResourceId id, m_Ctx.AddDockWindow(sv->Widget(), DockReference::AddTo, this); } +void PipelineStateViewer::EditOriginalShaderSource(ResourceId id, + const ShaderReflection *shaderDetails) +{ + QSet uniqueFiles; + rdcstrpairs files; + + for(const ShaderSourceFile &s : shaderDetails->debugInfo.files) + { + QString filename = s.filename; + + uint filenameHash = qHash(filename.toLower()); + + if(uniqueFiles.contains(filenameHash)) + { + qWarning() << lit("Duplicate full filename") << filename; + continue; + } + uniqueFiles.insert(filenameHash); + + files.push_back(make_rdcpair(s.filename, s.contents)); + } + + EditShader(id, shaderDetails->stage, shaderDetails->entryPoint, + shaderDetails->debugInfo.compileFlags, shaderDetails->debugInfo.encoding, files); +} + +void PipelineStateViewer::EditDecompiledSource(const ShaderProcessingTool &tool, ResourceId id, + const ShaderReflection *shaderDetails) +{ + QString source = tool.DisassembleShader(this, shaderDetails, ""); + + if(source.isEmpty()) + return; + + rdcstrpairs files; + files.push_back(make_rdcpair("decompiled", source)); + + EditShader(id, shaderDetails->stage, shaderDetails->entryPoint, + shaderDetails->debugInfo.compileFlags, tool.output, files); +} + +void PipelineStateViewer::SetupShaderEditButton(QToolButton *button, ResourceId pipelineId, + ResourceId shaderId, + const ShaderReflection *shaderDetails) +{ + if(!shaderDetails || !button->isEnabled() || button->popupMode() != QToolButton::MenuButtonPopup) + return; + + QMenu *menu = editMenus[(int)shaderDetails->stage]; + + menu->clear(); + + rdcarray accepted = m_Ctx.TargetShaderEncodings(); + + // if we have original source and it's in a known format, display it as the first most preferred + // option + if(!shaderDetails->debugInfo.files.empty() && + shaderDetails->debugInfo.encoding != ShaderEncoding::Unknown) + { + QAction *action = + new QAction(tr("Edit Source - %1").arg(shaderDetails->debugInfo.files[0].filename), menu); + action->setIcon(Icons::page_white_edit()); + + QObject::connect(action, &QAction::triggered, [this, shaderId, shaderDetails]() { + EditOriginalShaderSource(shaderId, shaderDetails); + }); + + menu->addAction(action); + } + + // next up, try the shader processing tools in order - all the ones that will decompile from our + // native representation. We don't check here yet if we have a valid compiler to compile from the + // output to what we want. + for(const ShaderProcessingTool &tool : m_Ctx.Config().ShaderProcessors) + { + // skip tools that can't decode our shader, or doesn't produce a textual output + if(tool.input != shaderDetails->encoding || !IsTextRepresentation(tool.output)) + continue; + + QAction *action = new QAction(tr("Decompile with %1").arg(tool.name), menu); + action->setIcon(Icons::page_white_edit()); + + QObject::connect(action, &QAction::triggered, [this, tool, shaderId, shaderDetails]() { + EditDecompiledSource(tool, shaderId, shaderDetails); + }); + + menu->addAction(action); + } + + // if all else fails we can generate a stub for editing. Skip this for GLSL as it always has + // source above which is preferred. + if(shaderDetails->encoding != ShaderEncoding::GLSL) + { + QString label = tr("Edit Generated Stub"); + + if(shaderDetails->encoding == ShaderEncoding::SPIRV) + label = tr("Edit Pseudocode"); + + QAction *action = new QAction(label, menu); + action->setIcon(Icons::page_white_edit()); + + QObject::connect(action, &QAction::triggered, [this, pipelineId, shaderId, shaderDetails]() { + QString entry; + QString src; + + if(shaderDetails->encoding == ShaderEncoding::SPIRV) + { + m_Ctx.Replay().AsyncInvoke([this, pipelineId, shaderId, shaderDetails](IReplayController *r) { + rdcstr disasm = r->DisassembleShader(pipelineId, shaderDetails, ""); + + QString editeddisasm = + tr("#### PSEUDOCODE SPIR-V DISASSEMBLY ###\n") + + tr("#### Use a SPIR-V decompiler to get compileable source ###\n\n"); + + editeddisasm += disasm; + + GUIInvoke::call(this, [this, shaderId, shaderDetails, editeddisasm]() { + rdcstrpairs files; + files.push_back(make_rdcpair("pseudocode", editeddisasm)); + + EditShader(shaderId, shaderDetails->stage, shaderDetails->entryPoint, + ShaderCompileFlags(), ShaderEncoding::Unknown, files); + }); + }); + } + else if(shaderDetails->encoding == ShaderEncoding::DXBC) + { + entry = lit("EditedShader%1S").arg(ToQStr(shaderDetails->stage, GraphicsAPI::D3D11)[0]); + + rdcstrpairs files; + files.push_back(make_rdcpair("decompiled_stub.hlsl", + GenerateHLSLStub(shaderDetails, entry))); + + EditShader(shaderId, shaderDetails->stage, entry, ShaderCompileFlags(), + ShaderEncoding::HLSL, files); + } + + }); + + menu->addAction(action); + } + + button->setMenu(menu); +} + bool PipelineStateViewer::SaveShaderFile(const ShaderReflection *shader) { if(!shader) diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h index e7fe9ba59..695ec14c7 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h @@ -35,6 +35,8 @@ class PipelineStateViewer; class QXmlStreamWriter; +class QToolButton; +class QMenu; class RDLabel; class D3D11PipelineStateViewer; @@ -63,14 +65,10 @@ public: void OnEventChanged(uint32_t eventId) override; QVariant persistData(); - void setPersistData(const QVariant &persistData); - bool PrepareShaderEditing(const ShaderReflection *shaderDetails, QString &entryFunc, - rdcstrpairs &files); - QString GenerateHLSLStub(const ShaderReflection *shaderDetails, const QString &entryFunc); - void EditShader(ShaderStage shaderType, ResourceId id, const ShaderReflection *shaderDetails, - const QString &entryFunc, ShaderEncoding encoding, const rdcstrpairs &files); + void SetupShaderEditButton(QToolButton *button, ResourceId pipelineId, ResourceId shaderId, + const ShaderReflection *shaderDetails); void setTopologyDiagram(QLabel *diagram, Topology topo); void setMeshViewPixmap(RDLabel *meshView); @@ -81,10 +79,24 @@ public: void exportHTMLTable(QXmlStreamWriter &xml, const QStringList &cols, const QVariantList &row); void endHTMLExport(QXmlStreamWriter *xml); +public slots: + void shaderEdit_clicked(); + private: Ui::PipelineStateViewer *ui; ICaptureContext &m_Ctx; + QMenu *editMenus[6] = {}; + + bool PrepareShaderEditing(const ShaderReflection *shaderDetails, QString &entryFunc, + rdcstrpairs &files); + QString GenerateHLSLStub(const ShaderReflection *shaderDetails, const QString &entryFunc); + void EditShader(ResourceId id, ShaderStage shaderType, const rdcstr &entry, + ShaderCompileFlags compileFlags, ShaderEncoding encoding, const rdcstrpairs &files); + void EditOriginalShaderSource(ResourceId id, const ShaderReflection *shaderDetails); + void EditDecompiledSource(const ShaderProcessingTool &tool, ResourceId id, + const ShaderReflection *shaderDetails); + void MakeShaderVariablesHLSL(bool cbufferContents, const rdcarray &vars, QString &struct_contents, QString &struct_defs); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index f8390b761..c6a86af44 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -138,7 +138,7 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx, } for(QToolButton *b : editButtons) - QObject::connect(b, &QToolButton::clicked, this, &VulkanPipelineStateViewer::shaderEdit_clicked); + QObject::connect(b, &QToolButton::clicked, &m_Common, &PipelineStateViewer::shaderEdit_clicked); for(QToolButton *b : saveButtons) QObject::connect(b, &QToolButton::clicked, this, &VulkanPipelineStateViewer::shaderSave_clicked); @@ -1771,6 +1771,8 @@ void VulkanPipelineStateViewer::setState() : state.graphics.pipelineResourceId; b->setEnabled(shaderDetails && pipe != ResourceId()); + + m_Common.SetupShaderEditButton(b, pipe, stage->resourceId, shaderDetails); } //////////////////////////////////////////////// @@ -2412,70 +2414,6 @@ void VulkanPipelineStateViewer::shaderView_clicked() m_Ctx.AddDockWindow(shad->Widget(), DockReference::AddTo, this); } -void VulkanPipelineStateViewer::shaderEdit_clicked() -{ - QWidget *sender = qobject_cast(QObject::sender()); - const VKPipe::Shader *stage = stageForSender(sender); - - if(!stage || stage->resourceId == ResourceId()) - return; - - const ShaderReflection *shaderDetails = stage->reflection; - - ResourceId pipe = stage->stage == ShaderStage::Compute - ? m_Ctx.CurVulkanPipelineState()->compute.pipelineResourceId - : m_Ctx.CurVulkanPipelineState()->graphics.pipelineResourceId; - - if(!shaderDetails) - return; - - QString entryFunc = lit("EditedShader%1S").arg(ToQStr(stage->stage, GraphicsAPI::Vulkan)[0]); - - rdcstrpairs files; - - bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files); - ShaderEncoding encoding = shaderDetails->debugInfo.encoding; - - if(hasOrigSource) - { - if(files.empty()) - return; - } - else - { - QString glsl; - - if(!m_Ctx.Config().SPIRVDisassemblers.isEmpty()) - glsl = m_Ctx.Config().SPIRVDisassemblers[0].DisassembleShader(this, shaderDetails); - - if(!glsl.isEmpty()) - { - // if we decompiled, we expect the entry point name to be the same and assume GLSL - // decompilation for now - entryFunc = shaderDetails->entryPoint; - encoding = ShaderEncoding::GLSL; - files.clear(); - files.push_back(make_rdcpair("decompiled", glsl)); - } - else - { - m_Ctx.Replay().AsyncInvoke([this, stage, pipe, shaderDetails, entryFunc](IReplayController *r) { - rdcstr disasm = r->DisassembleShader(pipe, shaderDetails, ""); - - GUIInvoke::call(this, [this, stage, shaderDetails, entryFunc, disasm]() { - rdcstrpairs fileMap; - fileMap.push_back(make_rdcpair("pseudo_disassembly", disasm)); - m_Common.EditShader(stage->stage, stage->resourceId, shaderDetails, entryFunc, - ShaderEncoding::Unknown, fileMap); - }); - }); - return; - } - } - - m_Common.EditShader(stage->stage, stage->resourceId, shaderDetails, entryFunc, encoding, files); -} - void VulkanPipelineStateViewer::shaderSave_clicked() { const VKPipe::Shader *stage = stageForSender(qobject_cast(QObject::sender())); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h index 0ef6e0234..35faae3bd 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h @@ -74,7 +74,6 @@ private slots: // manual slots void shaderView_clicked(); - void shaderEdit_clicked(); void shaderSave_clicked(); void resource_itemActivated(RDTreeWidgetItem *item, int column); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui index adf3f5b8e..44546fa1b 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui @@ -545,6 +545,9 @@ :/page_white_edit.png:/page_white_edit.png + + + QToolButton::MenuButtonPopup Qt::ToolButtonTextBesideIcon @@ -829,6 +832,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1112,6 +1118,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -1395,6 +1404,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -2438,6 +2450,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon @@ -3254,6 +3269,9 @@ :/page_white_edit.png:/page_white_edit.png + + QToolButton::MenuButtonPopup + Qt::ToolButtonTextBesideIcon diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 659b4cf1d..0570433b6 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -192,8 +192,29 @@ void ShaderViewer::editShader(bool customShader, ShaderStage stage, const QStrin m_Stage = stage; m_Flags = flags; - m_Encoding = shaderEncoding; - m_EntryPoint = entryPoint; + + // set up compilation parameters + QStringList strs; + strs.clear(); + for(ShaderEncoding i : values()) + strs << ToQStr(i); + + ui->encoding->addItems(strs); + ui->encoding->setCurrentIndex((int)shaderEncoding); + ui->entryFunc->setText(entryPoint); + + PopulateCompileTools(); + + QObject::connect(ui->encoding, OverloadedSlot::of(&QComboBox::currentIndexChanged), + [this](int) { PopulateCompileTools(); }); + QObject::connect(ui->compileTool, OverloadedSlot::of(&QComboBox::currentIndexChanged), + [this](int) { PopulateCompileToolParameters(); }); + + // if it's a custom shader, hide the group entirely (don't allow customisation of compile + // parameters). We can still use it to store the parameters passed in. When visible we collapse it + // by default. + if(customShader) + ui->compilationGroup->hide(); // hide debugging windows ui->watch->hide(); @@ -281,6 +302,17 @@ void ShaderViewer::editShader(bool customShader, ShaderStage stage, const QStrin ui->docking->areaOf(m_Scintillas.front()), 0.2f)); ui->docking->setToolWindowProperties( m_Errors, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + + if(!customShader) + { + ui->compilationGroup->setWindowTitle(tr("Compilation Settings")); + ui->docking->addToolWindow( + ui->compilationGroup, + ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->docking->areaOf(m_Errors))); + ui->docking->setToolWindowProperties( + ui->compilationGroup, + ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + } } void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader, @@ -294,6 +326,9 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR m_Stage = ShaderStage::Vertex; m_DebugContext = debugContext; + // no recompilation happening, hide that group + ui->compilationGroup->hide(); + // no replacing allowed, stay in find mode m_FindReplace->allowUserModeChange(false); @@ -311,20 +346,23 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR GUIInvoke::call(this, [this, targets, disasm]() { QStringList targetNames; - for(const rdcstr &t : targets) + for(int i = 0; i < targets.count(); i++) { - QString target = t; - targetNames << target; + QString target = targets[i]; + targetNames << QString(targets[i]); - // if we have a SPIR-V disassembly option, and the shader is natively SPIR-V, add our own - // SPIR-V disassemblers right after - if(target.contains(lit("SPIR-V")) && m_ShaderDetails->encoding == ShaderEncoding::SPIRV) + if(i == 0) { - for(const SPIRVDisassembler &d : m_Ctx.Config().SPIRVDisassemblers) - targetNames << targetName(d); + // add any custom decompiling tools we have after the first one + for(const ShaderProcessingTool &d : m_Ctx.Config().ShaderProcessors) + { + if(d.input == m_ShaderDetails->encoding) + targetNames << targetName(d); + } } } + m_DisassemblyType->clear(); m_DisassemblyType->addItems(targetNames); m_DisassemblyType->setCurrentIndex(0); QObject::connect(m_DisassemblyType, OverloadedSlot::of(&QComboBox::currentIndexChanged), @@ -1151,11 +1189,11 @@ void ShaderViewer::disassemble_typeChanged(int index) QString targetStr = m_DisassemblyType->currentText(); QByteArray target = targetStr.toUtf8(); - for(const SPIRVDisassembler &disasm : m_Ctx.Config().SPIRVDisassemblers) + for(const ShaderProcessingTool &disasm : m_Ctx.Config().ShaderProcessors) { if(targetStr == targetName(disasm)) { - QString result = disasm.DisassembleShader(this, m_ShaderDetails); + QString result = disasm.DisassembleShader(this, m_ShaderDetails, ""); m_DisassemblyView->setReadOnly(false); m_DisassemblyView->setText(result.toUtf8().data()); @@ -1499,9 +1537,9 @@ RDTreeWidgetItem *ShaderViewer::makeResourceRegister(const Bindpoint &bind, uint } } -QString ShaderViewer::targetName(const SPIRVDisassembler &disasm) +QString ShaderViewer::targetName(const ShaderProcessingTool &disasm) { - return lit("SPIR-V (%1)").arg(disasm.name); + return lit("%1 (%2)").arg(ToQStr(disasm.input)).arg(disasm.name); } void ShaderViewer::addFileList() @@ -2538,6 +2576,9 @@ void ShaderViewer::ShowErrors(const rdcstr &errors) m_Errors->setReadOnly(false); m_Errors->setText(errors.c_str()); m_Errors->setReadOnly(true); + + if(!errors.isEmpty()) + ToolWindowManager::raiseToolWindow(m_Errors); } } @@ -3186,6 +3227,80 @@ void ShaderViewer::on_findReplace_clicked() m_FindReplace->takeFocus(); } +void ShaderViewer::PopulateCompileTools() +{ + ShaderEncoding encoding = ShaderEncoding(ui->encoding->currentIndex()); + rdcarray accepted = m_Ctx.TargetShaderEncodings(); + + QStringList strs; + strs.clear(); + for(const ShaderProcessingTool &tool : m_Ctx.Config().ShaderProcessors) + { + // skip tools that can't accept our inputs, or doesn't produce a supported output + if(tool.input != encoding || accepted.indexOf(tool.output) < 0) + continue; + + strs << tool.name; + } + + // if we can pass in the shader source as-is, add a built-in option + if(accepted.indexOf(encoding) >= 0) + strs << tr("Builtin"); + + ui->compileTool->clear(); + ui->compileTool->addItems(strs); + + // pick the first option as highest priority + ui->compileTool->setCurrentIndex(0); + + // fill out parameters + PopulateCompileToolParameters(); + + if(strs.isEmpty()) + { + ShowErrors(tr("No compilation tool found that takes %1 as input and produces compatible output") + .arg(ToQStr(encoding))); + } +} + +void ShaderViewer::PopulateCompileToolParameters() +{ + ShaderEncoding encoding = ShaderEncoding(ui->encoding->currentIndex()); + rdcarray accepted = m_Ctx.TargetShaderEncodings(); + + ui->toolCommandLine->clear(); + + if(accepted.indexOf(encoding) >= 0 && + ui->compileTool->currentIndex() == ui->compileTool->count() - 1) + { + // if we're using the last Builtin tool, there are no default parameters + } + else + { + for(const ShaderProcessingTool &tool : m_Ctx.Config().ShaderProcessors) + { + if(QString(tool.name) == ui->compileTool->currentText()) + { + ui->toolCommandLine->setPlainText(tool.DefaultArguments()); + ui->toolCommandLine->setEnabled(true); + break; + } + } + } + + for(int i = 0; i < m_Flags.flags.count(); i++) + { + ShaderCompileFlag &flag = m_Flags.flags[i]; + if(flag.name == "@cmdline") + { + // append command line from saved flags + ui->toolCommandLine->setPlainText(ui->toolCommandLine->toPlainText() + + lit(" %1").arg(flag.value)); + break; + } + } +} + bool ShaderViewer::ProcessIncludeDirectives(QString &source, const rdcstrpairs &files) { // try and match up #includes against the files that we have. This isn't always @@ -3228,7 +3343,7 @@ bool ShaderViewer::ProcessIncludeDirectives(QString &source, const rdcstrpairs & if(source[ws] != QLatin1Char('<') && source[ws] != QLatin1Char('"')) { - ShowErrors(lit("Invalid #include directive found:\r\n") + line); + ShowErrors(tr("Invalid #include directive found:\r\n") + line); return false; } @@ -3238,7 +3353,7 @@ bool ShaderViewer::ProcessIncludeDirectives(QString &source, const rdcstrpairs & if(end == -1) { - ShowErrors(lit("Invalid #include directive found:\r\n") + line); + ShowErrors(tr("Invalid #include directive found:\r\n") + line); return false; } @@ -3299,9 +3414,18 @@ void ShaderViewer::on_refresh_clicked() return; } - if(m_SaveCallback) + // if we don't have any compile tools - even the 'builtin' one, this compilation is not going to + // succeed. + if(ui->compileTool->count() == 0) { - ShaderEncoding encoding = m_Encoding; + ShaderEncoding encoding = ShaderEncoding(ui->encoding->currentIndex()); + + ShowErrors(tr("No compilation tool found that takes %1 as input and produces compatible output") + .arg(ToQStr(encoding))); + } + else if(m_SaveCallback) + { + ShaderEncoding encoding = ShaderEncoding(ui->encoding->currentIndex()); rdcstrpairs files; for(ScintillaEdit *s : m_Scintillas) @@ -3325,7 +3449,52 @@ void ShaderViewer::on_refresh_clicked() bytebuf shaderBytes(source.toUtf8()); - m_SaveCallback(&m_Ctx, this, encoding, m_Flags, m_EntryPoint, shaderBytes); + rdcarray accepted = m_Ctx.TargetShaderEncodings(); + + if(accepted.indexOf(encoding) >= 0 && + ui->compileTool->currentIndex() == ui->compileTool->count() - 1) + { + // if using the builtin compiler, just pass through + } + else + { + for(const ShaderProcessingTool &tool : m_Ctx.Config().ShaderProcessors) + { + if(QString(tool.name) == ui->compileTool->currentText()) + { + bytebuf result = tool.CompileShader(this, source, ui->entryFunc->text(), m_Stage, + ui->toolCommandLine->toPlainText()); + + if(result.isEmpty()) + { + ShowErrors(tr("Error invoking '%1' to compile source").arg(tool.name)); + return; + } + + encoding = tool.output; + shaderBytes = result; + break; + } + } + } + + ShaderCompileFlags flags = m_Flags; + + bool found = false; + for(ShaderCompileFlag &f : flags.flags) + { + if(f.name == "@cmdline") + { + f.value = ui->toolCommandLine->toPlainText(); + found = true; + break; + } + } + + if(!found) + flags.flags.push_back({"@cmdline", ui->toolCommandLine->toPlainText()}); + + m_SaveCallback(&m_Ctx, this, encoding, flags, ui->entryFunc->text(), shaderBytes); } } diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index f9339566d..4c2f1be4d 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -160,6 +160,8 @@ private: ResourceId pipeline, ShaderDebugTrace *trace, const QString &debugContext); bool eventFilter(QObject *watched, QEvent *event) override; + void PopulateCompileTools(); + void PopulateCompileToolParameters(); bool ProcessIncludeDirectives(QString &source, const rdcstrpairs &files); const rdcarray *GetVariableList(VariableCategory varCat, int arrayIdx); @@ -186,8 +188,6 @@ private: ICaptureContext &m_Ctx; const ShaderBindpointMapping *m_Mapping = NULL; const ShaderReflection *m_ShaderDetails = NULL; - ShaderEncoding m_Encoding; - rdcstr m_EntryPoint; ShaderCompileFlags m_Flags; ShaderStage m_Stage; QString m_DebugContext; @@ -241,7 +241,7 @@ private: static const int INDICATOR_FINDRESULT = 0; static const int INDICATOR_REGHIGHLIGHT = 1; - QString targetName(const SPIRVDisassembler &disasm); + QString targetName(const ShaderProcessingTool &disasm); void addFileList(); diff --git a/qrenderdoc/Windows/ShaderViewer.ui b/qrenderdoc/Windows/ShaderViewer.ui index 5b4142af7..a33acfb0a 100644 --- a/qrenderdoc/Windows/ShaderViewer.ui +++ b/qrenderdoc/Windows/ShaderViewer.ui @@ -7,7 +7,7 @@ 0 0 963 - 574 + 746 @@ -592,8 +592,148 @@ true + + + + 60 + 560 + 441 + 141 + + + + + + + Compiler: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Source Type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + QComboBox::NoInsert + + + QComboBox::AdjustToContents + + + + + + + + 0 + 20 + + + + QComboBox::NoInsert + + + QComboBox::AdjustToContents + + + + + + + Entry Point: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 20 + + + + false + + + + + + + RDTableWidget + QTableWidget +
Widgets/Extended/RDTableWidget.h
+
RDTreeWidget QTreeView @@ -604,11 +744,6 @@ QWidget
3rdparty/toolwindowmanager/ToolWindowManager.h
- - RDTableWidget - QTableWidget -
Widgets/Extended/RDTableWidget.h
-
diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 6421e1f76..b186a5981 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -158,7 +158,7 @@ SOURCES += Code/qrenderdoc.cpp \ Code/pyrenderdoc/PythonContext.cpp \ Code/Interface/QRDInterface.cpp \ Code/Interface/Analytics.cpp \ - Code/Interface/SPIRVDisassembler.cpp \ + Code/Interface/ShaderProcessingTool.cpp \ Code/Interface/PersistantConfig.cpp \ Code/Interface/RemoteHost.cpp \ Styles/StyleData.cpp \ diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index 9516fcc8b..3b6fc3e0b 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -578,7 +578,7 @@ - + Create diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index 44d8f4c63..f4cf909a5 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -699,9 +699,6 @@ Windows\Dialogs - - Code\Interface - Widgets @@ -711,6 +708,9 @@ Generated Files + + Code\Interface + diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 04b616267..7a2c39b9f 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -1320,6 +1320,12 @@ enum class GPUVendor : uint32_t DECLARE_REFLECTION_ENUM(GPUVendor); +DOCUMENT(R"(Get the GPUVendor for a given PCI Vendor ID. + +:param int vendorID: The PCI Vendor ID +:return: The vendor identified +:rtype: GPUVendor +)"); constexpr GPUVendor GPUVendorFromPCIVendor(uint32_t vendorID) { // temporarily disable clang-format to make this more readable. @@ -1416,6 +1422,18 @@ enum class ShaderEncoding : uint32_t ITERABLE_OPERATORS(ShaderEncoding); DECLARE_REFLECTION_ENUM(ShaderEncoding); +DOCUMENT(R"(Check whether or not this is a human readable text representation. + +:param ShaderEncoding e: The encoding to check. +:return: ``True`` if it describes a text representation, ``False`` for a bytecode representation. +:rtype: ``bool`` +)"); +constexpr inline bool IsTextRepresentation(ShaderEncoding encoding) +{ + return encoding == ShaderEncoding::HLSL || encoding == ShaderEncoding::GLSL || + encoding == ShaderEncoding::SPIRVAsm; +} + DOCUMENT(R"(A primitive topology used for processing vertex data. .. data:: Unknown @@ -1683,7 +1701,7 @@ DOCUMENT(R"(Check whether or not this is a strip-type topology. :param Topology t: The topology to check. :return: ``True`` if it describes a strip topology, ``False`` for a list. -:rtype: ``int`` +:rtype: ``bool`` )"); constexpr inline bool IsStrip(Topology topology) {