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:
baldurk
2020-10-21 14:14:20 +01:00
parent 31bf9f4593
commit 806187f613
11 changed files with 429 additions and 71 deletions
+172 -2
View File
@@ -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,