mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-13 13:30:44 +00:00
Save and load edited shaders as capture modifications
* When a shader edit is loaded with a capture, it's loaded as "pending" and not immediately applied.
This commit is contained in:
@@ -978,6 +978,16 @@ void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const Repla
|
||||
bytebuf buf = access->GetSectionContents(idx);
|
||||
LoadNotes(QString::fromUtf8((const char *)buf.data(), buf.count()));
|
||||
}
|
||||
|
||||
idx = access->FindSectionByType(SectionType::EditedShaders);
|
||||
if(idx >= 0)
|
||||
{
|
||||
bytebuf buf = access->GetSectionContents(idx);
|
||||
GUIInvoke::call(m_MainWindow, [this, buf]() {
|
||||
LoadEdits(QString::fromUtf8((const char *)buf.data(), buf.count()));
|
||||
});
|
||||
}
|
||||
|
||||
QString driver = access->DriverName();
|
||||
if(driver == lit("Image"))
|
||||
{
|
||||
@@ -1480,6 +1490,11 @@ void CaptureContext::SetRemoteHost(int hostIdx)
|
||||
m_MainWindow->setRemoteHost(hostIdx);
|
||||
}
|
||||
|
||||
bool CaptureContext::IsResourceReplaced(ResourceId id)
|
||||
{
|
||||
return m_ReplacedResources.contains(id);
|
||||
}
|
||||
|
||||
void CaptureContext::RegisterReplacement(ResourceId id)
|
||||
{
|
||||
if(!m_ReplacedResources.contains(id))
|
||||
@@ -1596,6 +1611,9 @@ void CaptureContext::SaveChanges()
|
||||
if(m_CaptureMods & CaptureModifications::Notes)
|
||||
success &= SaveNotes();
|
||||
|
||||
if(m_CaptureMods & CaptureModifications::EditedShaders)
|
||||
success &= SaveEdits();
|
||||
|
||||
if(!success)
|
||||
{
|
||||
RDDialog::critical(m_MainWindow, tr("Can't save file"),
|
||||
@@ -1729,6 +1747,66 @@ void CaptureContext::LoadNotes(const QString &data)
|
||||
}
|
||||
}
|
||||
|
||||
bool CaptureContext::SaveEdits()
|
||||
{
|
||||
// make sure this format matches SetCaptureFileComments in app_api.cpp if it changes
|
||||
QVariantList editors;
|
||||
|
||||
for(ShaderViewer *e : m_ShaderEditors)
|
||||
{
|
||||
QVariantMap editor = e->SaveEditor();
|
||||
if(!editor.isEmpty())
|
||||
editors.push_back(e->SaveEditor());
|
||||
}
|
||||
|
||||
QVariantMap root;
|
||||
root[lit("editors")] = editors;
|
||||
|
||||
QString json = VariantToJSON(root);
|
||||
|
||||
SectionProperties props;
|
||||
props.type = SectionType::EditedShaders;
|
||||
props.version = 1;
|
||||
|
||||
return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8());
|
||||
}
|
||||
|
||||
void CaptureContext::LoadEdits(const QString &data)
|
||||
{
|
||||
QVariantMap root = JSONToVariant(data);
|
||||
|
||||
QVariantList editors = root[lit("editors")].toList();
|
||||
|
||||
auto replaceSaveCallback = [this](ICaptureContext *ctx, IShaderViewer *viewer, ResourceId id,
|
||||
ShaderStage stage, ShaderEncoding shaderEncoding,
|
||||
ShaderCompileFlags flags, rdcstr entryFunc, bytebuf shaderBytes) {
|
||||
|
||||
ApplyShaderEdit(viewer, id, stage, shaderEncoding, flags, entryFunc, shaderBytes);
|
||||
};
|
||||
|
||||
auto replaceCloseCallback = [this](ICaptureContext *ctx, IShaderViewer *view, ResourceId id) {
|
||||
RevertShaderEdit(view, id);
|
||||
};
|
||||
|
||||
for(QVariant e : editors)
|
||||
{
|
||||
ShaderViewer *edit =
|
||||
ShaderViewer::LoadEditor(*this, e.toMap(), replaceSaveCallback, replaceCloseCallback,
|
||||
[this](ShaderViewer *view, bool closed) {
|
||||
m_CaptureMods |= CaptureModifications::EditedShaders;
|
||||
if(closed)
|
||||
m_ShaderEditors.removeOne(view);
|
||||
},
|
||||
m_MainWindow->Widget());
|
||||
|
||||
if(edit)
|
||||
{
|
||||
AddDockWindow(edit->Widget(), DockReference::MainToolArea, NULL);
|
||||
m_ShaderEditors.push_back(edit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CaptureContext::OpenRGPProfile(const rdcstr &filename)
|
||||
{
|
||||
delete m_RGP;
|
||||
@@ -2195,8 +2273,100 @@ IShaderViewer *CaptureContext::EditShader(ResourceId id, ShaderStage stage,
|
||||
IShaderViewer::SaveCallback saveCallback,
|
||||
IShaderViewer::CloseCallback closeCallback)
|
||||
{
|
||||
return ShaderViewer::EditShader(*this, id, stage, entryPoint, files, shaderEncoding, flags,
|
||||
saveCallback, closeCallback, m_MainWindow->Widget());
|
||||
ShaderViewer *viewer = NULL;
|
||||
|
||||
if(id != ResourceId())
|
||||
{
|
||||
auto replaceSaveCallback = [this, saveCallback](
|
||||
ICaptureContext *ctx, IShaderViewer *viewer, ResourceId id, ShaderStage stage,
|
||||
ShaderEncoding shaderEncoding, ShaderCompileFlags flags, rdcstr entryFunc,
|
||||
bytebuf shaderBytes) {
|
||||
|
||||
ApplyShaderEdit(viewer, id, stage, shaderEncoding, flags, entryFunc, shaderBytes);
|
||||
|
||||
if(saveCallback)
|
||||
saveCallback(ctx, viewer, id, stage, shaderEncoding, flags, entryFunc, shaderBytes);
|
||||
};
|
||||
|
||||
auto replaceCloseCallback = [this, closeCallback](ICaptureContext *ctx, IShaderViewer *view,
|
||||
ResourceId id) {
|
||||
RevertShaderEdit(view, id);
|
||||
|
||||
if(closeCallback)
|
||||
closeCallback(ctx, view, id);
|
||||
};
|
||||
|
||||
viewer = ShaderViewer::EditShader(*this, id, stage, entryPoint, files, shaderEncoding, flags,
|
||||
replaceSaveCallback, replaceCloseCallback,
|
||||
[this](ShaderViewer *view, bool closed) {
|
||||
m_CaptureMods |= CaptureModifications::EditedShaders;
|
||||
if(closed)
|
||||
m_ShaderEditors.removeOne(view);
|
||||
},
|
||||
m_MainWindow->Widget());
|
||||
|
||||
m_ShaderEditors.push_back(viewer);
|
||||
m_CaptureMods |= CaptureModifications::EditedShaders;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewer = ShaderViewer::EditShader(*this, id, stage, entryPoint, files, shaderEncoding, flags,
|
||||
saveCallback, closeCallback, NULL, m_MainWindow->Widget());
|
||||
}
|
||||
|
||||
return viewer;
|
||||
}
|
||||
|
||||
void CaptureContext::ApplyShaderEdit(IShaderViewer *viewer, ResourceId id, ShaderStage stage,
|
||||
ShaderEncoding shaderEncoding, ShaderCompileFlags flags,
|
||||
const rdcstr &entryFunc, const bytebuf &shaderBytes)
|
||||
{
|
||||
if(shaderBytes.isEmpty())
|
||||
return;
|
||||
|
||||
ANALYTIC_SET(UIFeatures.ShaderEditing, true);
|
||||
|
||||
QPointer<QObject> ptr(viewer->Widget());
|
||||
|
||||
// invoke off to the ReplayController to replace the capture's shader
|
||||
// with our edited one
|
||||
Replay().AsyncInvoke([this, entryFunc, shaderBytes, shaderEncoding, flags, stage, id, ptr,
|
||||
viewer](IReplayController *r) {
|
||||
rdcstr errs;
|
||||
|
||||
ResourceId from = id;
|
||||
ResourceId to;
|
||||
|
||||
rdctie(to, errs) =
|
||||
r->BuildTargetShader(entryFunc.c_str(), shaderEncoding, shaderBytes, flags, stage);
|
||||
|
||||
if(to == ResourceId())
|
||||
{
|
||||
r->RemoveReplacement(from);
|
||||
|
||||
// this GUIInvoke call always needs to go through even if the viewer has been closed.
|
||||
GUIInvoke::call(GetMainWindow()->Widget(), [this, from]() { UnregisterReplacement(from); });
|
||||
}
|
||||
else
|
||||
{
|
||||
r->ReplaceResource(from, to);
|
||||
|
||||
GUIInvoke::call(GetMainWindow()->Widget(), [this, from]() { RegisterReplacement(from); });
|
||||
}
|
||||
if(ptr)
|
||||
GUIInvoke::call(ptr, [viewer, errs]() { viewer->ShowErrors(errs); });
|
||||
});
|
||||
}
|
||||
|
||||
void CaptureContext::RevertShaderEdit(IShaderViewer *viewer, ResourceId id)
|
||||
{
|
||||
// remove the replacement on close (we could make this more sophisticated if there
|
||||
// was a place to control replaced resources/shaders).
|
||||
Replay().AsyncInvoke([this, id](IReplayController *r) {
|
||||
if(IsCaptureLoaded())
|
||||
r->RemoveReplacement(id);
|
||||
GUIInvoke::call(GetMainWindow()->Widget(), [this, id] { UnregisterReplacement(id); });
|
||||
});
|
||||
}
|
||||
|
||||
IShaderViewer *CaptureContext::DebugShader(const ShaderBindpointMapping *bind,
|
||||
|
||||
@@ -52,6 +52,7 @@ class StatisticsViewer;
|
||||
class TimelineBar;
|
||||
class PythonShell;
|
||||
class ResourceInspector;
|
||||
class ShaderViewer;
|
||||
|
||||
class CaptureContext : public ICaptureContext, IExtensionManager
|
||||
{
|
||||
@@ -114,6 +115,7 @@ public:
|
||||
uint32_t eventId, bool force = false) override;
|
||||
void SetRemoteHost(int hostIndex);
|
||||
void RefreshStatus() override { SetEventID({}, m_SelectedEventID, m_EventID, true); }
|
||||
bool IsResourceReplaced(ResourceId id) override;
|
||||
void RegisterReplacement(ResourceId id) override;
|
||||
void UnregisterReplacement(ResourceId id) override;
|
||||
void RefreshUIStatus(const rdcarray<ICaptureViewer *> &exclude, bool updateSelectedEvent,
|
||||
@@ -241,6 +243,11 @@ public:
|
||||
ShaderCompileFlags flags, IShaderViewer::SaveCallback saveCallback,
|
||||
IShaderViewer::CloseCallback closeCallback) override;
|
||||
|
||||
void ApplyShaderEdit(IShaderViewer *viewer, ResourceId id, ShaderStage stage,
|
||||
ShaderEncoding shaderEncoding, ShaderCompileFlags flags,
|
||||
const rdcstr &entryFunc, const bytebuf &shaderBytes);
|
||||
void RevertShaderEdit(IShaderViewer *viewer, ResourceId id);
|
||||
|
||||
IShaderViewer *DebugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader,
|
||||
ResourceId pipeline, ShaderDebugTrace *trace,
|
||||
const rdcstr &debugContext) override;
|
||||
@@ -303,6 +310,9 @@ private:
|
||||
bool SaveNotes();
|
||||
void LoadNotes(const QString &data);
|
||||
|
||||
bool SaveEdits();
|
||||
void LoadEdits(const QString &data);
|
||||
|
||||
void CacheResources();
|
||||
rdcstr GetResourceNameUnsuffixed(const ResourceDescription *desc);
|
||||
|
||||
@@ -386,6 +396,8 @@ private:
|
||||
|
||||
QList<QPointer<RegisteredMenuItem>> m_RegisteredMenuItems;
|
||||
|
||||
QList<ShaderViewer *> m_ShaderEditors;
|
||||
|
||||
// Windows
|
||||
MainWindow *m_MainWindow = NULL;
|
||||
EventBrowser *m_EventBrowser = NULL;
|
||||
|
||||
@@ -564,6 +564,8 @@ DOCUMENT(R"(A shader window used for viewing, editing, or debugging.
|
||||
|
||||
:param CaptureContext context: The current capture context.
|
||||
:param ShaderViewer viewer: The open shader viewer.
|
||||
:param ResourceId id: The id of the shader being replaced.
|
||||
:param ShaderStage stage: The shader stage of the shader being replaced.
|
||||
:param ShaderEncoding encoding: The encoding of the files being passed.
|
||||
:param ShaderCompileFlags flags: The flags to use during compilation.
|
||||
:param str entryFunc: The name of the entry point.
|
||||
@@ -577,13 +579,15 @@ DOCUMENT(R"(A shader window used for viewing, editing, or debugging.
|
||||
Called whenever a shader viewer that was open for editing is closed.
|
||||
|
||||
:param CaptureContext context: The current capture context.
|
||||
:param ShaderViewer viewer: The open shader viewer.
|
||||
:param ResourceId id: The id of the shader being replaced.
|
||||
)");
|
||||
struct IShaderViewer
|
||||
{
|
||||
typedef std::function<void(ICaptureContext *ctx, IShaderViewer *, ShaderEncoding,
|
||||
ShaderCompileFlags, rdcstr, bytebuf)>
|
||||
typedef std::function<void(ICaptureContext *, IShaderViewer *, ResourceId, ShaderStage,
|
||||
ShaderEncoding, ShaderCompileFlags, rdcstr, bytebuf)>
|
||||
SaveCallback;
|
||||
typedef std::function<void(ICaptureContext *ctx)> CloseCallback;
|
||||
typedef std::function<void(ICaptureContext *, IShaderViewer *, ResourceId)> CloseCallback;
|
||||
|
||||
DOCUMENT(
|
||||
"Retrieves the QWidget for this :class:`ShaderViewer` if PySide2 is available, or otherwise "
|
||||
@@ -1011,6 +1015,10 @@ This is a bitmask, so several values can be present at once.
|
||||
|
||||
The general notes field has been changed.
|
||||
|
||||
.. data:: EditedShaders
|
||||
|
||||
There are shader editing changes (new edits or reverts).
|
||||
|
||||
.. data:: All
|
||||
|
||||
Fixed value with all bits set, indication all modifications have been made.
|
||||
@@ -1021,6 +1029,7 @@ enum class CaptureModifications : uint32_t
|
||||
Renames = 0x0001,
|
||||
Bookmarks = 0x0002,
|
||||
Notes = 0x0004,
|
||||
EditedShaders = 0x0008,
|
||||
All = 0xffffffff,
|
||||
};
|
||||
|
||||
@@ -1162,6 +1171,12 @@ been made.
|
||||
)");
|
||||
virtual void RefreshStatus() = 0;
|
||||
|
||||
DOCUMENT(R"(Determine if a resource has been replaced. See :meth:`RegisterReplacement`.
|
||||
|
||||
:param ResourceId id: The id of the resource to check.
|
||||
)");
|
||||
virtual bool IsResourceReplaced(ResourceId id) = 0;
|
||||
|
||||
DOCUMENT(R"(Register that a resource has replaced, so that the UI can be updated to reflect the
|
||||
change.
|
||||
|
||||
|
||||
@@ -1071,6 +1071,8 @@ bool MainWindow::PromptCloseCapture()
|
||||
text += tr("Bookmarks have been changed.\n");
|
||||
if(mods & CaptureModifications::Notes)
|
||||
text += tr("Capture notes have been changed.\n");
|
||||
if(mods & CaptureModifications::EditedShaders)
|
||||
text += tr("Edited shaders have been changed.\n");
|
||||
|
||||
bool saveas = false;
|
||||
|
||||
|
||||
@@ -837,60 +837,8 @@ IShaderViewer *PipelineStateViewer::EditShader(ResourceId id, ShaderStage shader
|
||||
const rdcstr &entry, ShaderCompileFlags compileFlags,
|
||||
ShaderEncoding encoding, const rdcstrpairs &files)
|
||||
{
|
||||
auto saveCallback = [shaderType, id](ICaptureContext *ctx, IShaderViewer *viewer,
|
||||
ShaderEncoding shaderEncoding, ShaderCompileFlags flags,
|
||||
rdcstr entryFunc, bytebuf shaderBytes) {
|
||||
if(shaderBytes.isEmpty())
|
||||
return;
|
||||
|
||||
ANALYTIC_SET(UIFeatures.ShaderEditing, true);
|
||||
|
||||
QPointer<QObject> ptr(viewer->Widget());
|
||||
|
||||
// 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,
|
||||
ptr, viewer](IReplayController *r) {
|
||||
rdcstr errs;
|
||||
|
||||
ResourceId from = id;
|
||||
ResourceId to;
|
||||
|
||||
rdctie(to, errs) =
|
||||
r->BuildTargetShader(entryFunc.c_str(), shaderEncoding, shaderBytes, flags, shaderType);
|
||||
|
||||
if(ptr)
|
||||
GUIInvoke::call(ptr, [viewer, errs]() { viewer->ShowErrors(errs); });
|
||||
if(to == ResourceId())
|
||||
{
|
||||
r->RemoveReplacement(from);
|
||||
|
||||
// this GUIInvoke call always needs to go through even if the viewer has been closed.
|
||||
GUIInvoke::call(ctx->GetMainWindow()->Widget(),
|
||||
[ctx, from]() { ctx->UnregisterReplacement(from); });
|
||||
}
|
||||
else
|
||||
{
|
||||
r->ReplaceResource(from, to);
|
||||
|
||||
GUIInvoke::call(ctx->GetMainWindow()->Widget(),
|
||||
[ctx, from]() { ctx->RegisterReplacement(from); });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
auto closeCallback = [id](ICaptureContext *ctx) {
|
||||
// remove the replacement on close (we could make this more sophisticated if there
|
||||
// was a place to control replaced resources/shaders).
|
||||
ctx->Replay().AsyncInvoke([ctx, id](IReplayController *r) {
|
||||
if(ctx->IsCaptureLoaded())
|
||||
r->RemoveReplacement(id);
|
||||
GUIInvoke::call(ctx->GetMainWindow()->Widget(), [ctx, id] { ctx->UnregisterReplacement(id); });
|
||||
});
|
||||
};
|
||||
|
||||
IShaderViewer *sv = m_Ctx.EditShader(id, shaderType, entry, files, encoding, compileFlags,
|
||||
saveCallback, closeCallback);
|
||||
IShaderViewer *sv =
|
||||
m_Ctx.EditShader(id, shaderType, entry, files, encoding, compileFlags, NULL, NULL);
|
||||
|
||||
m_Ctx.AddDockWindow(sv->Widget(), DockReference::AddTo, this);
|
||||
|
||||
|
||||
@@ -210,6 +210,10 @@ struct CaptureContextInvoker : ICaptureContext
|
||||
InvokeVoidFunction(&ICaptureContext::SetEventID, exclude, selectedEventID, eventId, force);
|
||||
}
|
||||
virtual void RefreshStatus() override { InvokeVoidFunction(&ICaptureContext::RefreshStatus); }
|
||||
virtual bool IsResourceReplaced(ResourceId id) override
|
||||
{
|
||||
return InvokeRetFunction<bool>(&ICaptureContext::IsResourceReplaced, id);
|
||||
}
|
||||
virtual void RegisterReplacement(ResourceId id) override
|
||||
{
|
||||
InvokeVoidFunction(&ICaptureContext::RegisterReplacement, id);
|
||||
|
||||
@@ -215,6 +215,7 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e
|
||||
m_Flags = flags;
|
||||
|
||||
m_CustomShader = (id == ResourceId());
|
||||
m_EditingShader = id;
|
||||
|
||||
// set up compilation parameters
|
||||
for(ShaderEncoding i : values<ShaderEncoding>())
|
||||
@@ -230,12 +231,22 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e
|
||||
ui->encoding->setCurrentIndex(m_Encodings.indexOf(shaderEncoding));
|
||||
ui->entryFunc->setText(entryPoint);
|
||||
|
||||
QObject::connect(ui->entryFunc, &QLineEdit::textChanged,
|
||||
[this](const QString &) { MarkModification(); });
|
||||
QObject::connect(ui->toolCommandLine, &QTextEdit::textChanged, [this]() { MarkModification(); });
|
||||
|
||||
PopulateCompileTools();
|
||||
|
||||
QObject::connect(ui->encoding, OverloadedSlot<int>::of(&QComboBox::currentIndexChanged),
|
||||
[this](int) { PopulateCompileTools(); });
|
||||
[this](int) {
|
||||
PopulateCompileTools();
|
||||
MarkModification();
|
||||
});
|
||||
QObject::connect(ui->compileTool, OverloadedSlot<int>::of(&QComboBox::currentIndexChanged),
|
||||
[this](int) { PopulateCompileToolParameters(); });
|
||||
[this](int) {
|
||||
PopulateCompileToolParameters();
|
||||
MarkModification();
|
||||
});
|
||||
|
||||
// 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
|
||||
@@ -289,6 +300,8 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e
|
||||
const QByteArray &, int, int, int) {
|
||||
if(type & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT | SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
|
||||
m_FindState = FindState();
|
||||
|
||||
MarkModification();
|
||||
});
|
||||
|
||||
m_Ctx.GetMainWindow()->RegisterShortcut(QKeySequence(QKeySequence::Refresh).toString(), this,
|
||||
@@ -961,6 +974,131 @@ void ShaderViewer::gotoDisassemblyDebugging()
|
||||
m_DisassemblyFrame->setFocus(Qt::MouseFocusReason);
|
||||
}
|
||||
|
||||
ShaderViewer *ShaderViewer::LoadEditor(ICaptureContext &ctx, QVariantMap data,
|
||||
IShaderViewer::SaveCallback saveCallback,
|
||||
IShaderViewer::CloseCallback closeCallback,
|
||||
ModifyCallback modifyCallback, QWidget *parent)
|
||||
{
|
||||
if(data.isEmpty())
|
||||
return NULL;
|
||||
|
||||
ResourceId id;
|
||||
|
||||
{
|
||||
QVariant v = data[lit("id")];
|
||||
RichResourceTextInitialise(v);
|
||||
id = v.value<ResourceId>();
|
||||
}
|
||||
ShaderStage stage = (ShaderStage)data[lit("stage")].toUInt();
|
||||
rdcstr entryPoint = data[lit("entryPoint")].toString();
|
||||
ShaderEncoding encoding = (ShaderEncoding)data[lit("encoding")].toUInt();
|
||||
QString commandLine = data[lit("commandLine")].toString();
|
||||
QString toolName = data[lit("tool")].toString();
|
||||
|
||||
ShaderCompileFlags flags;
|
||||
|
||||
{
|
||||
QVariantMap v = data[lit("flags")].toMap();
|
||||
|
||||
for(const QString &str : v.keys())
|
||||
flags.flags.push_back({(rdcstr)str, (rdcstr)v[str].toString()});
|
||||
}
|
||||
|
||||
rdcstrpairs files;
|
||||
|
||||
{
|
||||
QVariantList v = data[lit("files")].toList();
|
||||
|
||||
for(QVariant f : v)
|
||||
{
|
||||
QVariantMap file = f.toMap();
|
||||
files.push_back(
|
||||
{(rdcstr)file[lit("name")].toString(), (rdcstr)file[lit("contents")].toString()});
|
||||
}
|
||||
}
|
||||
|
||||
rdcstr errors;
|
||||
|
||||
if((uint32_t)encoding >= (uint32_t)ShaderEncoding::Count)
|
||||
{
|
||||
errors += tr("Unrecognised shader encoding '%1'").arg(ToStr(encoding));
|
||||
// with no other information let's guess HLSL as the most likely
|
||||
encoding = ShaderEncoding::HLSL;
|
||||
}
|
||||
|
||||
ShaderViewer *view = EditShader(ctx, id, stage, entryPoint, files, encoding, flags, saveCallback,
|
||||
closeCallback, modifyCallback, parent);
|
||||
|
||||
int toolIndex = -1;
|
||||
|
||||
for(int i = 0; i < view->ui->compileTool->count(); i++)
|
||||
{
|
||||
QString tool = view->ui->compileTool->itemText(i);
|
||||
if(tool == toolName)
|
||||
{
|
||||
toolIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(toolIndex == -1)
|
||||
{
|
||||
errors += tr("Unknown shader tool '%1'").arg(toolName);
|
||||
// let's pick the highest priority tool for the current encoding
|
||||
toolIndex = 0;
|
||||
}
|
||||
|
||||
view->ui->encoding->setCurrentIndex(view->m_Encodings.indexOf(encoding));
|
||||
view->ui->compileTool->setCurrentIndex(toolIndex);
|
||||
view->ui->entryFunc->setText(entryPoint);
|
||||
view->ui->toolCommandLine->setText(commandLine);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
QVariantMap ShaderViewer::SaveEditor()
|
||||
{
|
||||
QVariantMap ret;
|
||||
|
||||
if(m_EditingShader != ResourceId())
|
||||
{
|
||||
ret[lit("id")] = m_EditingShader;
|
||||
ret[lit("stage")] = (uint32_t)m_Stage;
|
||||
ret[lit("entryPoint")] = ui->entryFunc->text();
|
||||
ret[lit("encoding")] = (uint32_t)currentEncoding();
|
||||
ret[lit("commandLine")] = ui->toolCommandLine->toPlainText();
|
||||
ret[lit("tool")] = ui->compileTool->currentText();
|
||||
|
||||
{
|
||||
QVariantMap v;
|
||||
|
||||
for(const ShaderCompileFlag &flag : m_Flags.flags)
|
||||
v[flag.name] = QString(flag.value);
|
||||
|
||||
ret[lit("flags")] = v;
|
||||
}
|
||||
|
||||
{
|
||||
QVariantList v;
|
||||
|
||||
for(ScintillaEdit *edit : m_Scintillas)
|
||||
{
|
||||
QWidget *w = (QWidget *)edit;
|
||||
|
||||
QVariantMap f;
|
||||
f[lit("name")] = w->property("filename").toString();
|
||||
f[lit("contents")] = QString::fromUtf8(edit->getText(edit->textLength() + 1));
|
||||
|
||||
v.push_back(f);
|
||||
}
|
||||
|
||||
ret[lit("files")] = v;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ShaderViewer::~ShaderViewer()
|
||||
{
|
||||
delete m_FindResults;
|
||||
@@ -975,7 +1113,10 @@ ShaderViewer::~ShaderViewer()
|
||||
m_Ctx.Replay().AsyncInvoke([trace](IReplayController *r) { r->FreeTrace(trace); });
|
||||
|
||||
if(m_CloseCallback)
|
||||
m_CloseCallback(&m_Ctx);
|
||||
m_CloseCallback(&m_Ctx, this, m_EditingShader);
|
||||
|
||||
if(m_ModifyCallback)
|
||||
m_ModifyCallback(this, true);
|
||||
|
||||
m_Ctx.RemoveCaptureViewer(this);
|
||||
delete ui;
|
||||
@@ -2309,6 +2450,30 @@ bool ShaderViewer::getVar(RDTreeWidgetItem *item, ShaderVariable *var, QString *
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderViewer::setEditorWindowTitle()
|
||||
{
|
||||
if(m_EditingShader != ResourceId())
|
||||
{
|
||||
if(!m_Ctx.IsResourceReplaced(m_EditingShader))
|
||||
m_Modified = true;
|
||||
|
||||
if(m_Modified)
|
||||
{
|
||||
QString title = windowTitle();
|
||||
if(title[0] != QLatin1Char('*'))
|
||||
title.prepend(lit("* "));
|
||||
setWindowTitle(title);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString title = windowTitle();
|
||||
if(title[0] == QLatin1Char('*'))
|
||||
title.remove(0, 2);
|
||||
setWindowTitle(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderViewer::highlightMatchingVars(RDTreeWidgetItem *root, const QString varName,
|
||||
const QColor highlightColor)
|
||||
{
|
||||
@@ -3872,6 +4037,8 @@ void ShaderViewer::ShowErrors(const rdcstr &errors)
|
||||
if(!errors.isEmpty())
|
||||
ToolWindowManager::raiseToolWindow(m_Errors);
|
||||
}
|
||||
|
||||
setEditorWindowTitle();
|
||||
}
|
||||
|
||||
void ShaderViewer::AddWatch(const rdcstr &variable)
|
||||
@@ -4340,6 +4507,16 @@ bool ShaderViewer::eventFilter(QObject *watched, QEvent *event)
|
||||
return QFrame::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void ShaderViewer::MarkModification()
|
||||
{
|
||||
if(m_ModifyCallback)
|
||||
m_ModifyCallback(this, false);
|
||||
|
||||
m_Modified = true;
|
||||
|
||||
setEditorWindowTitle();
|
||||
}
|
||||
|
||||
void ShaderViewer::disasm_tooltipShow(int x, int y)
|
||||
{
|
||||
// do nothing if there's no trace
|
||||
@@ -4757,7 +4934,10 @@ void ShaderViewer::on_refresh_clicked()
|
||||
if(!found)
|
||||
flags.flags.push_back({"@cmdline", ui->toolCommandLine->toPlainText()});
|
||||
|
||||
m_SaveCallback(&m_Ctx, this, encoding, flags, ui->entryFunc->text(), shaderBytes);
|
||||
m_Modified = false;
|
||||
|
||||
m_SaveCallback(&m_Ctx, this, m_EditingShader, m_Stage, encoding, flags, ui->entryFunc->text(),
|
||||
shaderBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,15 +74,25 @@ class ShaderViewer : public QFrame, public IShaderViewer, public ICaptureViewer
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static IShaderViewer *EditShader(ICaptureContext &ctx, ResourceId id, ShaderStage stage,
|
||||
const QString &entryPoint, const rdcstrpairs &files,
|
||||
ShaderEncoding shaderEncoding, ShaderCompileFlags flags,
|
||||
IShaderViewer::SaveCallback saveCallback,
|
||||
IShaderViewer::CloseCallback closeCallback, QWidget *parent)
|
||||
typedef std::function<void(ShaderViewer *viewer, bool closed)> ModifyCallback;
|
||||
|
||||
static ShaderViewer *LoadEditor(ICaptureContext &ctx, QVariantMap data,
|
||||
IShaderViewer::SaveCallback saveCallback,
|
||||
IShaderViewer::CloseCallback closeCallback,
|
||||
ModifyCallback modifyCallback, QWidget *parent);
|
||||
QVariantMap SaveEditor();
|
||||
|
||||
static ShaderViewer *EditShader(ICaptureContext &ctx, ResourceId id, ShaderStage stage,
|
||||
const QString &entryPoint, const rdcstrpairs &files,
|
||||
ShaderEncoding shaderEncoding, ShaderCompileFlags flags,
|
||||
IShaderViewer::SaveCallback saveCallback,
|
||||
IShaderViewer::CloseCallback closeCallback,
|
||||
ModifyCallback modifyCallback, QWidget *parent)
|
||||
{
|
||||
ShaderViewer *ret = new ShaderViewer(ctx, parent);
|
||||
ret->m_SaveCallback = saveCallback;
|
||||
ret->m_CloseCallback = closeCallback;
|
||||
ret->m_ModifyCallback = modifyCallback;
|
||||
ret->editShader(id, stage, entryPoint, files, shaderEncoding, flags);
|
||||
return ret;
|
||||
}
|
||||
@@ -177,6 +187,8 @@ private:
|
||||
ResourceId pipeline, ShaderDebugTrace *trace, const QString &debugContext);
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
void MarkModification();
|
||||
|
||||
void PopulateCompileTools();
|
||||
void PopulateCompileToolParameters();
|
||||
bool ProcessIncludeDirectives(QString &source, const rdcstrpairs &files);
|
||||
@@ -207,6 +219,7 @@ private:
|
||||
ShaderBindpointMapping m_Mapping;
|
||||
const ShaderReflection *m_ShaderDetails = NULL;
|
||||
bool m_CustomShader = false;
|
||||
ResourceId m_EditingShader;
|
||||
ShaderCompileFlags m_Flags;
|
||||
QList<ShaderEncoding> m_Encodings;
|
||||
ShaderStage m_Stage;
|
||||
@@ -250,6 +263,9 @@ private:
|
||||
|
||||
SaveCallback m_SaveCallback;
|
||||
CloseCallback m_CloseCallback;
|
||||
ModifyCallback m_ModifyCallback;
|
||||
|
||||
bool m_Modified = true;
|
||||
|
||||
ShaderDebugTrace *m_Trace = NULL;
|
||||
rdcarray<ShaderDebugState> m_States;
|
||||
@@ -315,6 +331,8 @@ private:
|
||||
|
||||
void find(bool down);
|
||||
|
||||
void setEditorWindowTitle();
|
||||
|
||||
void runTo(QVector<size_t> runToInstructions, bool forward,
|
||||
ShaderEvents condition = ShaderEvents::NoEvent);
|
||||
|
||||
|
||||
@@ -4368,8 +4368,9 @@ void TextureViewer::on_customEdit_clicked()
|
||||
ResourceId(), ShaderStage::Fragment, lit("main"), files,
|
||||
encodingExtensions[QFileInfo(filename).completeSuffix()], ShaderCompileFlags(),
|
||||
// Save Callback
|
||||
[thisPointer, key, filename, path](ICaptureContext *ctx, IShaderViewer *viewer,
|
||||
ShaderEncoding, ShaderCompileFlags, rdcstr, bytebuf bytes) {
|
||||
[thisPointer, key, filename, path](ICaptureContext *ctx, IShaderViewer *viewer, ResourceId,
|
||||
ShaderStage, ShaderEncoding, ShaderCompileFlags, rdcstr,
|
||||
bytebuf bytes) {
|
||||
{
|
||||
// don't trigger a full refresh
|
||||
if(thisPointer)
|
||||
@@ -4397,7 +4398,7 @@ void TextureViewer::on_customEdit_clicked()
|
||||
}
|
||||
},
|
||||
|
||||
[thisPointer, key](ICaptureContext *ctx) {
|
||||
[thisPointer, key](ICaptureContext *, IShaderViewer *, ResourceId) {
|
||||
if(thisPointer)
|
||||
thisPointer->m_CustomShaderEditor.remove(key);
|
||||
});
|
||||
|
||||
@@ -1014,6 +1014,7 @@ rdcstr DoStringise(const SectionType &el)
|
||||
STRINGISE_ENUM_CLASS_NAMED(AMDRGPProfile, "amd/rgp/profile");
|
||||
STRINGISE_ENUM_CLASS_NAMED(ExtendedThumbnail, "renderdoc/internal/exthumb");
|
||||
STRINGISE_ENUM_CLASS_NAMED(EmbeddedLogfile, "renderdoc/internal/logfile");
|
||||
STRINGISE_ENUM_CLASS_NAMED(EditedShaders, "renderdoc/ui/edits");
|
||||
}
|
||||
END_ENUM_STRINGISE();
|
||||
}
|
||||
|
||||
@@ -92,6 +92,12 @@ version of RenderDoc that addes a new section type. They should be considered eq
|
||||
This section contains the log file at the time of capture, for debugging.
|
||||
|
||||
The name for this section will be "renderdoc/internal/logfile".
|
||||
|
||||
.. data:: EditedShaders
|
||||
|
||||
This section contains any edited shaders.
|
||||
|
||||
The name for this section will be "renderdoc/ui/edits".
|
||||
)");
|
||||
enum class SectionType : uint32_t
|
||||
{
|
||||
@@ -105,6 +111,7 @@ enum class SectionType : uint32_t
|
||||
AMDRGPProfile,
|
||||
ExtendedThumbnail,
|
||||
EmbeddedLogfile,
|
||||
EditedShaders,
|
||||
Count,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user