mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-25 16:06:31 +00:00
Update shader viewer to support editing and debugging of shaders
This commit is contained in:
@@ -23,6 +23,9 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "PipelineStateViewer.h"
|
||||
#include "3rdparty/toolwindowmanager/ToolWindowManager.h"
|
||||
#include "Windows/MainWindow.h"
|
||||
#include "Windows/ShaderViewer.h"
|
||||
#include "D3D11PipelineStateViewer.h"
|
||||
#include "D3D12PipelineStateViewer.h"
|
||||
#include "GLPipelineStateViewer.h"
|
||||
@@ -183,6 +186,188 @@ void PipelineStateViewer::setToVulkan()
|
||||
ui->layout->addWidget(m_Vulkan);
|
||||
m_Current = m_Vulkan;
|
||||
m_Ctx.CurPipelineState.DefaultType = eGraphicsAPI_Vulkan;
|
||||
}
|
||||
|
||||
bool PipelineStateViewer::PrepareShaderEditing(const ShaderReflection *shaderDetails,
|
||||
QString &entryFunc, QStringMap &files,
|
||||
QString &mainfile)
|
||||
{
|
||||
if(!shaderDetails->DebugInfo.entryFunc.empty() && !shaderDetails->DebugInfo.files.empty())
|
||||
{
|
||||
entryFunc = ToQStr(shaderDetails->DebugInfo.entryFunc);
|
||||
|
||||
QStringList uniqueFiles;
|
||||
|
||||
for(auto &s : shaderDetails->DebugInfo.files)
|
||||
{
|
||||
QString filename = ToQStr(s.first);
|
||||
if(uniqueFiles.contains(filename.toLower()))
|
||||
{
|
||||
qWarning() << "Duplicate full filename" << ToQStr(s.first);
|
||||
continue;
|
||||
}
|
||||
uniqueFiles.push_back(filename.toLower());
|
||||
|
||||
files[filename] = ToQStr(s.second);
|
||||
}
|
||||
|
||||
int entryFile = shaderDetails->DebugInfo.entryFile;
|
||||
if(entryFile < 0 || entryFile >= shaderDetails->DebugInfo.files.count)
|
||||
entryFile = 0;
|
||||
|
||||
mainfile = ToQStr(shaderDetails->DebugInfo.files[entryFile].first);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PipelineStateViewer::EditShader(ShaderStageType shaderType, ResourceId id,
|
||||
const ShaderReflection *shaderDetails, const QString &entryFunc,
|
||||
const QStringMap &files, const QString &mainfile)
|
||||
{
|
||||
ShaderViewer *sv = ShaderViewer::editShader(
|
||||
m_Ctx, false, entryFunc, files,
|
||||
// save callback
|
||||
[entryFunc, mainfile, shaderType, id, shaderDetails](
|
||||
CaptureContext *ctx, ShaderViewer *viewer, const QStringMap &updatedfiles) {
|
||||
QString compileSource = updatedfiles[mainfile];
|
||||
|
||||
// try and match up #includes against the files that we have. This isn't always
|
||||
// possible as fxc only seems to include the source for files if something in
|
||||
// that file was included in the compiled output. So you might end up with
|
||||
// dangling #includes - we just have to ignore them
|
||||
int offs = compileSource.indexOf("#include");
|
||||
|
||||
while(offs >= 0)
|
||||
{
|
||||
// search back to ensure this is a valid #include (ie. not in a comment).
|
||||
// Must only see whitespace before, then a newline.
|
||||
int ws = qMax(0, offs - 1);
|
||||
while(ws >= 0 && (compileSource[ws] == ' ' || compileSource[ws] == '\t'))
|
||||
ws--;
|
||||
|
||||
// not valid? jump to next.
|
||||
if(ws > 0 && compileSource[ws] != '\n')
|
||||
{
|
||||
offs = compileSource.indexOf("#include", offs + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int start = ws + 1;
|
||||
|
||||
bool tail = true;
|
||||
|
||||
int lineEnd = compileSource.indexOf("\n", start + 1);
|
||||
if(lineEnd == -1)
|
||||
{
|
||||
lineEnd = compileSource.length();
|
||||
tail = false;
|
||||
}
|
||||
|
||||
ws = offs + sizeof("#include") - 1;
|
||||
while(compileSource[ws] == ' ' || compileSource[ws] == '\t')
|
||||
ws++;
|
||||
|
||||
QString line = compileSource.mid(offs, lineEnd - offs + 1);
|
||||
|
||||
if(compileSource[ws] != '<' && compileSource[ws] != '"')
|
||||
{
|
||||
viewer->showErrors("Invalid #include directive found:\r\n" + line);
|
||||
return;
|
||||
}
|
||||
|
||||
// find matching char, either <> or "";
|
||||
int end = compileSource.indexOf(compileSource[ws] == '"' ? '"' : '>', ws + 1);
|
||||
|
||||
if(end == -1)
|
||||
{
|
||||
viewer->showErrors("Invalid #include directive found:\r\n" + line);
|
||||
return;
|
||||
}
|
||||
|
||||
QString fname = compileSource.mid(ws + 1, end - ws - 1);
|
||||
|
||||
QString fileText;
|
||||
|
||||
// look for exact match first
|
||||
if(updatedfiles.contains(fname))
|
||||
{
|
||||
fileText = updatedfiles[fname];
|
||||
}
|
||||
else
|
||||
{
|
||||
QString search = QFileInfo(fname).fileName();
|
||||
// if not, try and find the same filename (this is not proper include handling!)
|
||||
for(const QString &k : updatedfiles.keys())
|
||||
{
|
||||
if(QFileInfo(k).fileName().compare(search, Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
fileText = updatedfiles[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(fileText == "")
|
||||
fileText = "// Can't find file " + fname + "\n";
|
||||
}
|
||||
|
||||
compileSource = compileSource.left(offs) + "\n\n" + fileText + "\n\n" +
|
||||
(tail ? compileSource.mid(lineEnd + 1) : "");
|
||||
|
||||
// need to start searching from the beginning - wasteful but allows nested includes to
|
||||
// work
|
||||
offs = compileSource.indexOf("#include");
|
||||
}
|
||||
|
||||
if(updatedfiles.contains("@cmdline"))
|
||||
compileSource = updatedfiles["@cmdline"] + "\n\n" + compileSource;
|
||||
|
||||
// invoke off to the ReplayRenderer to replace the log's shader
|
||||
// with our edited one
|
||||
ctx->Renderer().AsyncInvoke([ctx, entryFunc, compileSource, shaderType, id, shaderDetails,
|
||||
viewer](IReplayRenderer *r) {
|
||||
rdctype::str errs;
|
||||
|
||||
uint flags = shaderDetails->DebugInfo.compileFlags;
|
||||
|
||||
ResourceId from = id;
|
||||
ResourceId to = r->BuildTargetShader(
|
||||
entryFunc.toUtf8().data(), compileSource.toUtf8().data(), flags, shaderType, &errs);
|
||||
|
||||
GUIInvoke::call([viewer, errs]() { viewer->showErrors(ToQStr(errs)); });
|
||||
if(to == ResourceId())
|
||||
{
|
||||
r->RemoveReplacement(from);
|
||||
GUIInvoke::call([ctx]() { ctx->RefreshStatus(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
r->ReplaceResource(from, to);
|
||||
GUIInvoke::call([ctx]() { ctx->RefreshStatus(); });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Close Callback
|
||||
[id](CaptureContext *ctx) {
|
||||
// remove the replacement on close (we could make this more sophisticated if there
|
||||
// was a place to control replaced resources/shaders).
|
||||
ctx->Renderer().AsyncInvoke([ctx, id](IReplayRenderer *r) {
|
||||
r->RemoveReplacement(id);
|
||||
GUIInvoke::call([ctx] { ctx->RefreshStatus(); });
|
||||
});
|
||||
},
|
||||
m_Ctx.mainWindow());
|
||||
|
||||
m_Ctx.setupDockWindow(sv);
|
||||
|
||||
ToolWindowManager *manager = ToolWindowManager::managerOf(this);
|
||||
|
||||
ToolWindowManager::AreaReference ref(ToolWindowManager::AddTo, manager->areaOf(this));
|
||||
manager->addToolWindow(sv, ref);
|
||||
}
|
||||
|
||||
bool PipelineStateViewer::SaveShaderFile(const ShaderReflection *shader)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user