mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-01 12:20:56 +00:00
Update shader viewer to support editing and debugging of shaders
This commit is contained in:
@@ -2149,11 +2149,13 @@ void D3D11PipelineStateViewer::shaderView_clicked()
|
||||
{
|
||||
ShaderStageType shaderStage = eShaderStage_Vertex;
|
||||
ShaderReflection *shaderDetails = NULL;
|
||||
const ShaderBindpointMapping *bindMap = NULL;
|
||||
|
||||
QWidget *sender = qobject_cast<QWidget *>(QObject::sender());
|
||||
if(sender == ui->iaBytecode || sender == ui->iaBytecodeViewButton)
|
||||
{
|
||||
shaderDetails = m_Ctx.CurD3D11PipelineState.m_IA.Bytecode;
|
||||
bindMap = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2162,11 +2164,13 @@ void D3D11PipelineStateViewer::shaderView_clicked()
|
||||
if(stage == NULL || stage->Shader == ResourceId())
|
||||
return;
|
||||
|
||||
bindMap = &stage->BindpointMapping;
|
||||
shaderDetails = stage->ShaderDetails;
|
||||
shaderStage = stage->stage;
|
||||
}
|
||||
|
||||
ShaderViewer *shad = new ShaderViewer(m_Ctx, shaderDetails, shaderStage, NULL, "");
|
||||
ShaderViewer *shad =
|
||||
ShaderViewer::viewShader(m_Ctx, bindMap, shaderDetails, shaderStage, m_Ctx.mainWindow());
|
||||
|
||||
m_Ctx.setupDockWindow(shad);
|
||||
|
||||
@@ -2178,6 +2182,38 @@ void D3D11PipelineStateViewer::shaderView_clicked()
|
||||
|
||||
void D3D11PipelineStateViewer::shaderEdit_clicked()
|
||||
{
|
||||
QWidget *sender = qobject_cast<QWidget *>(QObject::sender());
|
||||
const D3D11PipelineState::ShaderStage *stage = stageForSender(sender);
|
||||
|
||||
if(!stage || stage->Shader == ResourceId())
|
||||
return;
|
||||
|
||||
const ShaderReflection *shaderDetails = stage->ShaderDetails;
|
||||
|
||||
if(!shaderDetails)
|
||||
return;
|
||||
|
||||
QString entryFunc = QString("EditedShader%1S").arg(ToQStr(stage->stage, eGraphicsAPI_D3D11)[0]);
|
||||
|
||||
QString mainfile = "";
|
||||
|
||||
QStringMap files;
|
||||
|
||||
bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files, mainfile);
|
||||
|
||||
if(!hasOrigSource)
|
||||
{
|
||||
QString hlsl = "// TODO - generate stub HLSL";
|
||||
|
||||
mainfile = "generated.hlsl";
|
||||
|
||||
files[mainfile] = hlsl;
|
||||
}
|
||||
|
||||
if(files.empty())
|
||||
return;
|
||||
|
||||
m_Common.EditShader(stage->stage, stage->Shader, shaderDetails, entryFunc, files, mainfile);
|
||||
}
|
||||
|
||||
void D3D11PipelineStateViewer::shaderSave_clicked()
|
||||
|
||||
@@ -2160,7 +2160,8 @@ void D3D12PipelineStateViewer::shaderView_clicked()
|
||||
if(stage == NULL || stage->Shader == ResourceId())
|
||||
return;
|
||||
|
||||
ShaderViewer *shad = new ShaderViewer(m_Ctx, stage->ShaderDetails, stage->stage, NULL, "");
|
||||
ShaderViewer *shad = ShaderViewer::viewShader(
|
||||
m_Ctx, &stage->BindpointMapping, stage->ShaderDetails, stage->stage, m_Ctx.mainWindow());
|
||||
|
||||
m_Ctx.setupDockWindow(shad);
|
||||
|
||||
@@ -2172,6 +2173,38 @@ void D3D12PipelineStateViewer::shaderView_clicked()
|
||||
|
||||
void D3D12PipelineStateViewer::shaderEdit_clicked()
|
||||
{
|
||||
QWidget *sender = qobject_cast<QWidget *>(QObject::sender());
|
||||
const D3D12PipelineState::ShaderStage *stage = stageForSender(sender);
|
||||
|
||||
if(!stage || stage->Shader == ResourceId())
|
||||
return;
|
||||
|
||||
const ShaderReflection *shaderDetails = stage->ShaderDetails;
|
||||
|
||||
if(!shaderDetails)
|
||||
return;
|
||||
|
||||
QString entryFunc = QString("EditedShader%1S").arg(ToQStr(stage->stage, eGraphicsAPI_D3D12)[0]);
|
||||
|
||||
QString mainfile = "";
|
||||
|
||||
QStringMap files;
|
||||
|
||||
bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files, mainfile);
|
||||
|
||||
if(!hasOrigSource)
|
||||
{
|
||||
QString hlsl = "// TODO - generate stub HLSL";
|
||||
|
||||
mainfile = "generated.hlsl";
|
||||
|
||||
files[mainfile] = hlsl;
|
||||
}
|
||||
|
||||
if(files.empty())
|
||||
return;
|
||||
|
||||
m_Common.EditShader(stage->stage, stage->Shader, shaderDetails, entryFunc, files, mainfile);
|
||||
}
|
||||
|
||||
void D3D12PipelineStateViewer::shaderSave_clicked()
|
||||
|
||||
@@ -2257,7 +2257,8 @@ void GLPipelineStateViewer::shaderView_clicked()
|
||||
|
||||
ShaderReflection *shaderDetails = stage->ShaderDetails;
|
||||
|
||||
ShaderViewer *shad = new ShaderViewer(m_Ctx, shaderDetails, stage->stage, NULL, "");
|
||||
ShaderViewer *shad = ShaderViewer::viewShader(m_Ctx, &stage->BindpointMapping, shaderDetails,
|
||||
stage->stage, m_Ctx.mainWindow());
|
||||
|
||||
m_Ctx.setupDockWindow(shad);
|
||||
|
||||
@@ -2269,6 +2270,38 @@ void GLPipelineStateViewer::shaderView_clicked()
|
||||
|
||||
void GLPipelineStateViewer::shaderEdit_clicked()
|
||||
{
|
||||
QWidget *sender = qobject_cast<QWidget *>(QObject::sender());
|
||||
const GLPipelineState::ShaderStage *stage = stageForSender(sender);
|
||||
|
||||
if(!stage || stage->Shader == ResourceId())
|
||||
return;
|
||||
|
||||
const ShaderReflection *shaderDetails = stage->ShaderDetails;
|
||||
|
||||
if(!shaderDetails)
|
||||
return;
|
||||
|
||||
QString entryFunc = QString("EditedShader%1S").arg(ToQStr(stage->stage, eGraphicsAPI_OpenGL)[0]);
|
||||
|
||||
QString mainfile = "";
|
||||
|
||||
QStringMap files;
|
||||
|
||||
bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files, mainfile);
|
||||
|
||||
if(!hasOrigSource)
|
||||
{
|
||||
QString glsl = "// TODO - disassemble SPIR-V";
|
||||
|
||||
mainfile = "generated.glsl";
|
||||
|
||||
files[mainfile] = glsl;
|
||||
}
|
||||
|
||||
if(files.empty())
|
||||
return;
|
||||
|
||||
m_Common.EditShader(stage->stage, stage->Shader, shaderDetails, entryFunc, files, mainfile);
|
||||
}
|
||||
|
||||
void GLPipelineStateViewer::shaderSave_clicked()
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -56,6 +56,10 @@ public:
|
||||
void setPersistData(const QVariant &persistData);
|
||||
|
||||
bool SaveShaderFile(const ShaderReflection *shader);
|
||||
bool PrepareShaderEditing(const ShaderReflection *shaderDetails, QString &entryFunc,
|
||||
QStringMap &files, QString &mainfile);
|
||||
void EditShader(ShaderStageType shaderType, ResourceId id, const ShaderReflection *shaderDetails,
|
||||
const QString &entryFunc, const QStringMap &files, const QString &mainfile);
|
||||
|
||||
private:
|
||||
Ui::PipelineStateViewer *ui;
|
||||
|
||||
@@ -2359,7 +2359,8 @@ void VulkanPipelineStateViewer::shaderView_clicked()
|
||||
|
||||
ShaderReflection *shaderDetails = stage->ShaderDetails;
|
||||
|
||||
ShaderViewer *shad = new ShaderViewer(m_Ctx, shaderDetails, stage->stage, NULL, "");
|
||||
ShaderViewer *shad = ShaderViewer::viewShader(m_Ctx, &stage->BindpointMapping, shaderDetails,
|
||||
stage->stage, m_Ctx.mainWindow());
|
||||
|
||||
m_Ctx.setupDockWindow(shad);
|
||||
|
||||
@@ -2371,6 +2372,38 @@ void VulkanPipelineStateViewer::shaderView_clicked()
|
||||
|
||||
void VulkanPipelineStateViewer::shaderEdit_clicked()
|
||||
{
|
||||
QWidget *sender = qobject_cast<QWidget *>(QObject::sender());
|
||||
const VulkanPipelineState::ShaderStage *stage = stageForSender(sender);
|
||||
|
||||
if(!stage || stage->Shader == ResourceId())
|
||||
return;
|
||||
|
||||
const ShaderReflection *shaderDetails = stage->ShaderDetails;
|
||||
|
||||
if(!shaderDetails)
|
||||
return;
|
||||
|
||||
QString entryFunc = QString("EditedShader%1S").arg(ToQStr(stage->stage, eGraphicsAPI_Vulkan)[0]);
|
||||
|
||||
QString mainfile = "";
|
||||
|
||||
QStringMap files;
|
||||
|
||||
bool hasOrigSource = m_Common.PrepareShaderEditing(shaderDetails, entryFunc, files, mainfile);
|
||||
|
||||
if(!hasOrigSource)
|
||||
{
|
||||
QString glsl = "// TODO - disassemble SPIR-V";
|
||||
|
||||
mainfile = "generated.glsl";
|
||||
|
||||
files[mainfile] = glsl;
|
||||
}
|
||||
|
||||
if(files.empty())
|
||||
return;
|
||||
|
||||
m_Common.EditShader(stage->stage, stage->Shader, shaderDetails, entryFunc, files, mainfile);
|
||||
}
|
||||
|
||||
void VulkanPipelineStateViewer::shaderSave_clicked()
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QMenu>
|
||||
#include "3rdparty/toolwindowmanager/ToolWindowManager.h"
|
||||
#include "Windows/BufferViewer.h"
|
||||
#include "Windows/ShaderViewer.h"
|
||||
#include "ui_PixelHistoryView.h"
|
||||
|
||||
struct EventTag
|
||||
@@ -635,7 +636,42 @@ void PixelHistoryView::setHistory(const rdctype::array<PixelModification> &histo
|
||||
void PixelHistoryView::startDebug(EventTag tag)
|
||||
{
|
||||
m_Ctx.SetEventID({this}, tag.eventID, tag.eventID);
|
||||
// TODO Shader debugging
|
||||
|
||||
ShaderDebugTrace *trace = new ShaderDebugTrace;
|
||||
|
||||
bool success = false;
|
||||
|
||||
m_Ctx.Renderer().BlockInvoke([this, &success, trace](IReplayRenderer *r) {
|
||||
success =
|
||||
r->DebugPixel((uint32_t)m_Pixel.x(), (uint32_t)m_Pixel.y(), m_Display.sampleIdx, ~0U, trace);
|
||||
});
|
||||
|
||||
if(!success || trace->states.count == 0)
|
||||
{
|
||||
RDDialog::critical(this, tr("Debug Error"), tr("Error debugging pixel."));
|
||||
delete trace;
|
||||
return;
|
||||
}
|
||||
|
||||
GUIInvoke::call([this, trace]() {
|
||||
QString debugContext = QString("Pixel %1,%2").arg(m_Pixel.x()).arg(m_Pixel.y());
|
||||
|
||||
const ShaderReflection *shaderDetails =
|
||||
m_Ctx.CurPipelineState.GetShaderReflection(eShaderStage_Pixel);
|
||||
const ShaderBindpointMapping &bindMapping =
|
||||
m_Ctx.CurPipelineState.GetBindpointMapping(eShaderStage_Pixel);
|
||||
|
||||
// viewer takes ownership of the trace
|
||||
ShaderViewer *s = ShaderViewer::debugShader(m_Ctx, &bindMapping, shaderDetails,
|
||||
eShaderStage_Pixel, trace, debugContext, this);
|
||||
|
||||
m_Ctx.setupDockWindow(s);
|
||||
|
||||
ToolWindowManager *manager = ToolWindowManager::managerOf(this);
|
||||
|
||||
ToolWindowManager::AreaReference ref(ToolWindowManager::AddTo, manager->areaOf(this));
|
||||
manager->addToolWindow(s, ref);
|
||||
});
|
||||
}
|
||||
|
||||
void PixelHistoryView::jumpToPrimitive(EventTag tag)
|
||||
|
||||
+845
-100
File diff suppressed because it is too large
Load Diff
@@ -37,13 +37,45 @@ struct ShaderDebugTrace;
|
||||
struct ShaderReflection;
|
||||
class ScintillaEdit;
|
||||
|
||||
// from Scintilla
|
||||
typedef intptr_t sptr_t;
|
||||
|
||||
class ShaderViewer : public QFrame, public ILogViewerForm
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ShaderViewer(CaptureContext &ctx, ShaderReflection *shader, ShaderStageType stage,
|
||||
ShaderDebugTrace *trace, const QString &debugContext, QWidget *parent = 0);
|
||||
typedef std::function<void(CaptureContext *ctx, ShaderViewer *, const QStringMap &)> SaveMethod;
|
||||
typedef std::function<void(CaptureContext *ctx)> CloseMethod;
|
||||
|
||||
static ShaderViewer *editShader(CaptureContext &ctx, bool customShader, const QString &entryPoint,
|
||||
const QStringMap &files, SaveMethod saveCallback,
|
||||
CloseMethod closeCallback, QWidget *parent)
|
||||
{
|
||||
ShaderViewer *ret = new ShaderViewer(ctx, parent);
|
||||
ret->m_SaveCallback = saveCallback;
|
||||
ret->m_CloseCallback = closeCallback;
|
||||
ret->editShader(customShader, entryPoint, files);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ShaderViewer *debugShader(CaptureContext &ctx, const ShaderBindpointMapping *bind,
|
||||
const ShaderReflection *shader, ShaderStageType stage,
|
||||
ShaderDebugTrace *trace, const QString &debugContext,
|
||||
QWidget *parent)
|
||||
{
|
||||
ShaderViewer *ret = new ShaderViewer(ctx, parent);
|
||||
ret->debugShader(bind, shader, stage, trace, debugContext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ShaderViewer *viewShader(CaptureContext &ctx, const ShaderBindpointMapping *bind,
|
||||
const ShaderReflection *shader, ShaderStageType stage,
|
||||
QWidget *parent)
|
||||
{
|
||||
return ShaderViewer::debugShader(ctx, bind, shader, stage, NULL, "", parent);
|
||||
}
|
||||
|
||||
~ShaderViewer();
|
||||
|
||||
void OnLogfileLoaded();
|
||||
@@ -51,22 +83,69 @@ public:
|
||||
void OnSelectedEventChanged(uint32_t eventID) {}
|
||||
void OnEventChanged(uint32_t eventID);
|
||||
|
||||
int currentStep();
|
||||
void setCurrentStep(int step);
|
||||
|
||||
void toggleBreakpoint(int instruction = -1);
|
||||
|
||||
void showErrors(const QString &errors);
|
||||
|
||||
private slots:
|
||||
// automatic slots
|
||||
void on_findReplace_clicked();
|
||||
void on_save_clicked();
|
||||
void on_intView_clicked();
|
||||
void on_floatView_clicked();
|
||||
|
||||
// manual slots
|
||||
void disassembly_keyPressed(QKeyEvent *event);
|
||||
void readonly_keyPressed(QKeyEvent *event);
|
||||
void editable_keyPressed(QKeyEvent *event);
|
||||
void disassembly_buttonReleased(QMouseEvent *event);
|
||||
|
||||
public slots:
|
||||
bool stepBack();
|
||||
bool stepNext();
|
||||
void runToCursor();
|
||||
void runToSample();
|
||||
void runToNanOrInf();
|
||||
void runBack();
|
||||
void run();
|
||||
|
||||
private:
|
||||
explicit ShaderViewer(CaptureContext &ctx, QWidget *parent = 0);
|
||||
void editShader(bool customShader, const QString &entryPoint, const QStringMap &files);
|
||||
void debugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader,
|
||||
ShaderStageType stage, ShaderDebugTrace *trace, const QString &debugContext);
|
||||
|
||||
Ui::ShaderViewer *ui;
|
||||
CaptureContext &m_Ctx;
|
||||
ShaderDebugTrace *m_Trace;
|
||||
ShaderReflection *m_ShaderDetails;
|
||||
ScintillaEdit *m_DisassemblyView;
|
||||
const ShaderBindpointMapping *m_Mapping = NULL;
|
||||
const ShaderReflection *m_ShaderDetails = NULL;
|
||||
ShaderStageType m_Stage;
|
||||
ScintillaEdit *m_DisassemblyView = NULL;
|
||||
ScintillaEdit *m_Errors = NULL;
|
||||
QList<ScintillaEdit *> m_Scintillas;
|
||||
|
||||
SaveMethod m_SaveCallback;
|
||||
CloseMethod m_CloseCallback;
|
||||
|
||||
ShaderDebugTrace *m_Trace = NULL;
|
||||
int m_CurrentStep;
|
||||
QList<int> m_Breakpoints;
|
||||
|
||||
static const int CURRENT_MARKER = 0;
|
||||
static const int BREAKPOINT_MARKER = 1;
|
||||
static const int FINISHED_MARKER = 2;
|
||||
static const int BREAKPOINT_MARKER = 2;
|
||||
static const int FINISHED_MARKER = 4;
|
||||
|
||||
ScintillaEdit *MakeEditor(const QString &name, const QString &text, bool src);
|
||||
ScintillaEdit *AddFileScintilla(const QString &name, const QString &text);
|
||||
|
||||
int instructionForLine(sptr_t line);
|
||||
|
||||
void updateDebugging();
|
||||
void runTo(int runToInstruction, bool forward, ShaderDebugStateFlags condition = eShaderDbg_None);
|
||||
|
||||
QString stringRep(const ShaderVariable &var, bool useType);
|
||||
QTreeWidgetItem *makeResourceRegister(const BindpointMap &bind, uint32_t idx,
|
||||
const BoundResource &ro, const ShaderResource &resources);
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>691</width>
|
||||
<height>427</height>
|
||||
<width>963</width>
|
||||
<height>574</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -16,18 +16,18 @@
|
||||
<widget class="ToolWindowManager" name="docking" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
<x>80</x>
|
||||
<y>90</y>
|
||||
<width>351</width>
|
||||
<height>301</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTreeWidget" name="inputSig">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>60</x>
|
||||
<y>200</y>
|
||||
<x>570</x>
|
||||
<y>160</y>
|
||||
<width>256</width>
|
||||
<height>192</height>
|
||||
</rect>
|
||||
@@ -107,8 +107,8 @@
|
||||
<widget class="QTreeWidget" name="outputSig">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>200</y>
|
||||
<x>590</x>
|
||||
<y>360</y>
|
||||
<width>256</width>
|
||||
<height>192</height>
|
||||
</rect>
|
||||
@@ -185,14 +185,399 @@
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="RDTreeWidget" name="constants">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>310</y>
|
||||
<width>256</width>
|
||||
<height>192</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::PreventContextMenu</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="RDTreeWidget" name="variables">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>310</y>
|
||||
<width>256</width>
|
||||
<height>192</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QFrame" name="toolbar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>10</y>
|
||||
<width>291</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>28</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="findReplace">
|
||||
<property name="toolTip">
|
||||
<string>Find & Replace</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/find.png</normaloff>:/find.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="editSep">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="save">
|
||||
<property name="toolTip">
|
||||
<string>Compile & Save changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/save.png</normaloff>:/save.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="snippets">
|
||||
<property name="toolTip">
|
||||
<string>Insert built-in snippets</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/plugin_add.png</normaloff>:/plugin_add.png</iconset>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="debugSep">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="stepBack">
|
||||
<property name="toolTip">
|
||||
<string>Step Back (Shift-F10)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/stepprev.png</normaloff>:/stepprev.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="stepNext">
|
||||
<property name="toolTip">
|
||||
<string>Step Next (F10)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/stepnext.png</normaloff>:/stepnext.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="runToCursor">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>23</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Run to Cursor (Ctrl-F10)</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/runcursor.png</normaloff>:/runcursor.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="runToSample">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>23</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Run to Sample/Load/Gather</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/runsample.png</normaloff>:/runsample.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="runToNaNOrInf">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>23</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Run to NaN or Inf</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/runnaninf.png</normaloff>:/runnaninf.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="regFormatSep">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="intView">
|
||||
<property name="text">
|
||||
<string>int</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="floatView">
|
||||
<property name="text">
|
||||
<string>float</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="RDTableWidget" name="watch">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>710</x>
|
||||
<y>20</y>
|
||||
<width>151</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RDTreeWidget</class>
|
||||
<extends>QTreeWidget</extends>
|
||||
<header>Widgets/Extended/RDTreeWidget.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ToolWindowManager</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>3rdparty/toolwindowmanager/ToolWindowManager.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RDTableWidget</class>
|
||||
<extends>QTableWidget</extends>
|
||||
<header>Widgets/Extended/RDTableWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../Resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "BufferViewer.h"
|
||||
#include "MainWindow.h"
|
||||
#include "PixelHistoryView.h"
|
||||
#include "ShaderViewer.h"
|
||||
#include "ui_TextureViewer.h"
|
||||
|
||||
float area(const QSizeF &s)
|
||||
@@ -3267,6 +3268,52 @@ void TextureViewer::on_saveTex_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void TextureViewer::on_debugPixelContext_clicked()
|
||||
{
|
||||
ShaderDebugTrace *trace = new ShaderDebugTrace;
|
||||
|
||||
if(m_PickedPoint.x() < 0 || m_PickedPoint.y() < 0)
|
||||
return;
|
||||
|
||||
int x = m_PickedPoint.x() >> (int)m_TexDisplay.mip;
|
||||
int y = m_PickedPoint.y() >> (int)m_TexDisplay.mip;
|
||||
|
||||
bool success = false;
|
||||
|
||||
m_Ctx.Renderer().BlockInvoke([this, x, y, &success, trace](IReplayRenderer *r) {
|
||||
success = r->DebugPixel((uint32_t)x, (uint32_t)y, m_TexDisplay.sampleIdx, ~0U, trace);
|
||||
});
|
||||
|
||||
if(!success || trace->states.count == 0)
|
||||
{
|
||||
delete trace;
|
||||
|
||||
// if we couldn't debug the pixel on this event, open up a pixel history
|
||||
on_pixelHistory_clicked();
|
||||
return;
|
||||
}
|
||||
|
||||
GUIInvoke::call([this, x, y, trace]() {
|
||||
QString debugContext = tr("Pixel %1,%2").arg(x).arg(y);
|
||||
|
||||
const ShaderReflection *shaderDetails =
|
||||
m_Ctx.CurPipelineState.GetShaderReflection(eShaderStage_Pixel);
|
||||
const ShaderBindpointMapping &bindMapping =
|
||||
m_Ctx.CurPipelineState.GetBindpointMapping(eShaderStage_Pixel);
|
||||
|
||||
// viewer takes ownership of the trace
|
||||
ShaderViewer *s = ShaderViewer::debugShader(m_Ctx, &bindMapping, shaderDetails,
|
||||
eShaderStage_Pixel, trace, debugContext, this);
|
||||
|
||||
m_Ctx.setupDockWindow(s);
|
||||
|
||||
ToolWindowManager *manager = ToolWindowManager::managerOf(this);
|
||||
|
||||
ToolWindowManager::AreaReference ref(ToolWindowManager::AddTo, manager->areaOf(this));
|
||||
manager->addToolWindow(s, ref);
|
||||
});
|
||||
}
|
||||
|
||||
void TextureViewer::on_pixelHistory_clicked()
|
||||
{
|
||||
FetchTexture *texptr = GetCurrentTexture();
|
||||
|
||||
@@ -159,6 +159,7 @@ private slots:
|
||||
void on_viewTexBuffer_clicked();
|
||||
void on_texListShow_clicked();
|
||||
void on_saveTex_clicked();
|
||||
void on_debugPixelContext_clicked();
|
||||
void on_pixelHistory_clicked();
|
||||
|
||||
void on_cancelTextureListFilter_clicked();
|
||||
|
||||
Reference in New Issue
Block a user