mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-26 08:26:50 +00:00
Implement editing GL shaders in-place via resource replacements
This commit is contained in:
@@ -1381,6 +1381,179 @@ void WrappedOpenGL::RenderOverlayStr(float x, float y, const char *text)
|
||||
gl.glDrawArraysInstanced(eGL_TRIANGLE_STRIP, 0, 4, (GLsizei)len);
|
||||
}
|
||||
|
||||
struct ReplacementSearch
|
||||
{
|
||||
bool operator()(const pair<ResourceId, Replacement> &a, ResourceId b)
|
||||
{
|
||||
return a.first < b;
|
||||
}
|
||||
};
|
||||
|
||||
void WrappedOpenGL::ReplaceResource(ResourceId from, ResourceId to)
|
||||
{
|
||||
RemoveReplacement(from);
|
||||
|
||||
if(GetResourceManager()->HasLiveResource(from))
|
||||
{
|
||||
GLResource resource = GetResourceManager()->GetLiveResource(to);
|
||||
ResourceId livefrom = GetResourceManager()->GetLiveID(from);
|
||||
|
||||
if(resource.Namespace == eResShader)
|
||||
{
|
||||
// need to replace all programs that use this shader
|
||||
for(auto it=m_Programs.begin(); it != m_Programs.end(); ++it)
|
||||
{
|
||||
ResourceId progsrcid = it->first;
|
||||
ProgramData &progdata = it->second;
|
||||
|
||||
// see if the shader is used
|
||||
for(int i=0; i < 6; i++)
|
||||
{
|
||||
if(progdata.stageShaders[i] == livefrom)
|
||||
{
|
||||
GLuint progsrc = GetResourceManager()->GetCurrentResource(progsrcid).name;
|
||||
|
||||
// make a new program
|
||||
GLuint progdst = glCreateProgram();
|
||||
|
||||
ResourceId progdstid = GetResourceManager()->GetID(ProgramRes(GetCtx(), progdst));
|
||||
|
||||
// attach all but the i'th shader
|
||||
for(int j=0; j < 6; j++)
|
||||
if(i != j && progdata.stageShaders[j] != ResourceId())
|
||||
glAttachShader(progdst, GetResourceManager()->GetCurrentResource(progdata.stageShaders[j]).name);
|
||||
|
||||
// attach the new shader
|
||||
glAttachShader(progdst, resource.name);
|
||||
|
||||
// mark separable if previous program was separable
|
||||
GLint sep = 0;
|
||||
glGetProgramiv(progsrc, eGL_PROGRAM_SEPARABLE, &sep);
|
||||
|
||||
if(sep)
|
||||
glProgramParameteri(progdst, eGL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||
|
||||
ResourceId vs = progdata.stageShaders[0];
|
||||
ResourceId fs = progdata.stageShaders[4];
|
||||
|
||||
if(vs != ResourceId())
|
||||
{
|
||||
ShaderReflection *refl = &m_Shaders[vs].reflection;
|
||||
|
||||
// copy over attrib bindings
|
||||
for(int32_t i=0; i < refl->InputSig.count; i++)
|
||||
{
|
||||
// skip built-ins
|
||||
if(refl->InputSig[i].systemValue != eAttr_None)
|
||||
continue;
|
||||
|
||||
GLint idx = glGetAttribLocation(progsrc, refl->InputSig[i].varName.elems);
|
||||
glBindAttribLocation(progdst, (GLuint)idx, refl->InputSig[i].varName.elems);
|
||||
}
|
||||
}
|
||||
|
||||
if(fs != ResourceId())
|
||||
{
|
||||
ShaderReflection *refl = &m_Shaders[fs].reflection;
|
||||
|
||||
// copy over fragdata bindings
|
||||
for(int32_t i=0; i < refl->OutputSig.count; i++)
|
||||
{
|
||||
// only look at colour outputs (should be the only outputs from fs)
|
||||
if(refl->OutputSig[i].systemValue != eAttr_ColourOutput)
|
||||
continue;
|
||||
|
||||
GLint idx = glGetFragDataLocation(progsrc, refl->OutputSig[i].varName.elems);
|
||||
glBindFragDataLocation(progdst, (GLuint)idx, refl->OutputSig[i].varName.elems);
|
||||
}
|
||||
}
|
||||
|
||||
// link new program
|
||||
glLinkProgram(progdst);
|
||||
|
||||
GLint status = 0;
|
||||
glGetProgramiv(progdst, eGL_LINK_STATUS, &status);
|
||||
|
||||
if(status == 0)
|
||||
{
|
||||
GLint len = 1024;
|
||||
glGetProgramiv(progdst, eGL_INFO_LOG_LENGTH, &len);
|
||||
char *buffer = new char[len+1];
|
||||
glGetProgramInfoLog(progdst, len, NULL, buffer); buffer[len] = 0;
|
||||
|
||||
RDCWARN("When making program replacement for shader, program failed to link. Skipping replacement:\n%s", buffer);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
glDeleteProgram(progdst);
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy uniforms
|
||||
CopyProgramUniforms(m_Real, progsrc, progdst);
|
||||
|
||||
// replaceresource
|
||||
GetResourceManager()->ReplaceResource(GetResourceManager()->GetOriginalID(progsrcid), progdstid);
|
||||
|
||||
// insert into m_DependentReplacements
|
||||
auto insertPos = std::lower_bound(m_DependentReplacements.begin(), m_DependentReplacements.end(), from, ReplacementSearch());
|
||||
m_DependentReplacements.insert(insertPos, std::make_pair(from, Replacement(progsrcid, ProgramRes(GetCtx(), progdst))));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetResourceManager()->ReplaceResource(from, to);
|
||||
}
|
||||
|
||||
void WrappedOpenGL::RemoveReplacement(ResourceId id)
|
||||
{
|
||||
GetResourceManager()->RemoveReplacement(id);
|
||||
|
||||
// check if there are any dependent replacements, remove if so
|
||||
auto it = std::lower_bound(m_DependentReplacements.begin(), m_DependentReplacements.end(), id, ReplacementSearch());
|
||||
for(; it != m_DependentReplacements.end(); )
|
||||
{
|
||||
GetResourceManager()->RemoveReplacement(it->second.id);
|
||||
|
||||
switch(it->second.res.Namespace)
|
||||
{
|
||||
case eResProgram:
|
||||
glDeleteProgram(it->second.res.name);
|
||||
break;
|
||||
default:
|
||||
RDCERR("Unexpected resource type to be freed");
|
||||
break;
|
||||
}
|
||||
|
||||
it = m_DependentReplacements.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void WrappedOpenGL::FreeTargetResource(ResourceId id)
|
||||
{
|
||||
if(GetResourceManager()->HasLiveResource(id))
|
||||
{
|
||||
GLResource resource = GetResourceManager()->GetLiveResource(id);
|
||||
|
||||
RDCASSERT(resource.Namespace != eResUnknown);
|
||||
|
||||
switch(resource.Namespace)
|
||||
{
|
||||
case eResShader:
|
||||
glDeleteShader(resource.name);
|
||||
break;
|
||||
default:
|
||||
RDCERR("Unexpected resource type to be freed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WrappedOpenGL::Present(void *windowHandle)
|
||||
{
|
||||
RenderDoc::Inst().SetCurrentDriver(RDC_OpenGL);
|
||||
|
||||
@@ -86,6 +86,13 @@ struct DrawcallTreeNode
|
||||
}
|
||||
};
|
||||
|
||||
struct Replacement
|
||||
{
|
||||
Replacement(ResourceId i, GLResource r) : id(i), res(r) {}
|
||||
ResourceId id;
|
||||
GLResource res;
|
||||
};
|
||||
|
||||
class WrappedOpenGL
|
||||
{
|
||||
private:
|
||||
@@ -252,6 +259,7 @@ class WrappedOpenGL
|
||||
map<ResourceId, ShaderData> m_Shaders;
|
||||
map<ResourceId, ProgramData> m_Programs;
|
||||
map<ResourceId, PipelineData> m_Pipelines;
|
||||
vector< pair<ResourceId, Replacement> > m_DependentReplacements;
|
||||
|
||||
GLuint m_FakeBB_FBO;
|
||||
GLuint m_FakeBB_Color;
|
||||
@@ -333,6 +341,10 @@ class WrappedOpenGL
|
||||
|
||||
ContextData &GetCtxData();
|
||||
GLuint GetUniformProgram();
|
||||
|
||||
void ReplaceResource(ResourceId from, ResourceId to);
|
||||
void RemoveReplacement(ResourceId id);
|
||||
void FreeTargetResource(ResourceId id);
|
||||
|
||||
static const int FONT_TEX_WIDTH = 256;
|
||||
static const int FONT_TEX_HEIGHT = 128;
|
||||
|
||||
@@ -2600,6 +2600,69 @@ void GLReplay::FreeCustomShader(ResourceId id)
|
||||
m_pDriver->glDeleteProgram(m_pDriver->GetResourceManager()->GetCurrentResource(id).name);
|
||||
}
|
||||
|
||||
void GLReplay::BuildTargetShader(string source, string entry, const uint32_t compileFlags, ShaderStageType type, ResourceId *id, string *errors)
|
||||
{
|
||||
if(id == NULL || errors == NULL)
|
||||
{
|
||||
if(id) *id = ResourceId();
|
||||
return;
|
||||
}
|
||||
|
||||
WrappedOpenGL &gl = *m_pDriver;
|
||||
|
||||
MakeCurrentReplayContext(m_DebugCtx);
|
||||
|
||||
GLenum shtype = eGL_VERTEX_SHADER;
|
||||
switch(type)
|
||||
{
|
||||
default: RDCWARN("Unknown shader type %u", type);
|
||||
case eShaderStage_Vertex: shtype = eGL_VERTEX_SHADER; break;
|
||||
case eShaderStage_Tess_Control: shtype = eGL_TESS_CONTROL_SHADER; break;
|
||||
case eShaderStage_Tess_Eval: shtype = eGL_TESS_EVALUATION_SHADER; break;
|
||||
case eShaderStage_Geometry: shtype = eGL_GEOMETRY_SHADER; break;
|
||||
case eShaderStage_Fragment: shtype = eGL_FRAGMENT_SHADER; break;
|
||||
case eShaderStage_Compute: shtype = eGL_COMPUTE_SHADER; break;
|
||||
}
|
||||
|
||||
const char *src = source.c_str();
|
||||
GLuint shader = gl.glCreateShader(shtype);
|
||||
gl.glShaderSource(shader, 1, &src, NULL);
|
||||
gl.glCompileShader(shader);
|
||||
|
||||
GLint status = 0;
|
||||
gl.glGetShaderiv(shader, eGL_COMPILE_STATUS, &status);
|
||||
|
||||
if(errors)
|
||||
{
|
||||
GLint len = 1024;
|
||||
gl.glGetShaderiv(shader, eGL_INFO_LOG_LENGTH, &len);
|
||||
char *buffer = new char[len+1];
|
||||
gl.glGetShaderInfoLog(shader, len, NULL, buffer); buffer[len] = 0;
|
||||
*errors = buffer;
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
if(status == 0)
|
||||
*id = ResourceId();
|
||||
else
|
||||
*id = m_pDriver->GetResourceManager()->GetID(ShaderRes(m_pDriver->GetCtx(), shader));
|
||||
}
|
||||
|
||||
void GLReplay::ReplaceResource(ResourceId from, ResourceId to)
|
||||
{
|
||||
m_pDriver->ReplaceResource(from, to);
|
||||
}
|
||||
|
||||
void GLReplay::RemoveReplacement(ResourceId id)
|
||||
{
|
||||
m_pDriver->RemoveReplacement(id);
|
||||
}
|
||||
|
||||
void GLReplay::FreeTargetResource(ResourceId id)
|
||||
{
|
||||
m_pDriver->FreeTargetResource(id);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -2643,29 +2706,6 @@ void GLReplay::SetContextFilter(ResourceId id, uint32_t firstDefEv, uint32_t las
|
||||
|
||||
|
||||
|
||||
void GLReplay::BuildTargetShader(string source, string entry, const uint32_t compileFlags, ShaderStageType type, ResourceId *id, string *errors)
|
||||
{
|
||||
RDCUNIMPLEMENTED("BuildTargetShader");
|
||||
}
|
||||
|
||||
void GLReplay::ReplaceResource(ResourceId from, ResourceId to)
|
||||
{
|
||||
RDCUNIMPLEMENTED("ReplaceResource");
|
||||
}
|
||||
|
||||
void GLReplay::RemoveReplacement(ResourceId id)
|
||||
{
|
||||
RDCUNIMPLEMENTED("RemoveReplacement");
|
||||
}
|
||||
|
||||
void GLReplay::FreeTargetResource(ResourceId id)
|
||||
{
|
||||
RDCUNIMPLEMENTED("FreeTargetResource");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
vector<PixelModification> GLReplay::PixelHistory(uint32_t frameID, vector<EventUsage> events, ResourceId target, uint32_t x, uint32_t y, uint32_t sampleIdx)
|
||||
{
|
||||
RDCUNIMPLEMENTED("GLReplay::PixelHistory");
|
||||
|
||||
@@ -1749,6 +1749,61 @@ namespace renderdocui.Windows.PipelineState
|
||||
|
||||
private void shaderedit_Click(object sender, EventArgs e)
|
||||
{
|
||||
GLPipelineState.ShaderStage stage = GetStageForSender(sender);
|
||||
|
||||
if (stage == null) return;
|
||||
|
||||
ShaderReflection shaderDetails = stage.ShaderDetails;
|
||||
|
||||
if (stage.Shader == ResourceId.Null || shaderDetails == null) return;
|
||||
|
||||
var files = new Dictionary<string, string>();
|
||||
foreach (var s in shaderDetails.DebugInfo.files)
|
||||
files.Add(Path.GetFileName(s.filename), s.filetext);
|
||||
|
||||
if (files.Count == 0)
|
||||
return;
|
||||
|
||||
ShaderViewer sv = new ShaderViewer(m_Core, false, "main", files,
|
||||
|
||||
// Save Callback
|
||||
(ShaderViewer viewer, Dictionary<string, string> updatedfiles) =>
|
||||
{
|
||||
string compileSource = updatedfiles.First().Value;
|
||||
|
||||
// invoke off to the ReplayRenderer to replace the log's shader
|
||||
// with our edited one
|
||||
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
||||
{
|
||||
string errs = "";
|
||||
|
||||
ResourceId from = stage.Shader;
|
||||
ResourceId to = r.BuildTargetShader("main", compileSource, shaderDetails.DebugInfo.compileFlags, stage.stage, out errs);
|
||||
|
||||
viewer.BeginInvoke((MethodInvoker)delegate { viewer.ShowErrors(errs); });
|
||||
if (to == ResourceId.Null)
|
||||
{
|
||||
r.RemoveReplacement(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
r.ReplaceResource(from, to);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Close Callback
|
||||
() =>
|
||||
{
|
||||
// remove the replacement on close (we could make this more sophisticated if there
|
||||
// was a place to control replaced resources/shaders).
|
||||
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
||||
{
|
||||
r.RemoveReplacement(stage.Shader);
|
||||
});
|
||||
});
|
||||
|
||||
sv.Show(m_DockContent.DockPanel);
|
||||
}
|
||||
|
||||
private void ShowCBuffer(GLPipelineState.ShaderStage stage, UInt32 slot)
|
||||
|
||||
Reference in New Issue
Block a user