Implement editing GL shaders in-place via resource replacements

This commit is contained in:
baldurk
2015-02-10 16:46:33 +00:00
parent 54ad900f53
commit be6cbf4536
4 changed files with 303 additions and 23 deletions
+173
View File
@@ -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);
+12
View File
@@ -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;
+63 -23
View File
@@ -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)