From be6cbf4536158ccb961d7ab8505a6efe0899e6ae Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 10 Feb 2015 16:46:33 +0000 Subject: [PATCH] Implement editing GL shaders in-place via resource replacements --- renderdoc/driver/gl/gl_driver.cpp | 173 ++++++++++++++++++ renderdoc/driver/gl/gl_driver.h | 12 ++ renderdoc/driver/gl/gl_replay.cpp | 86 ++++++--- .../PipelineState/GLPipelineStateViewer.cs | 55 ++++++ 4 files changed, 303 insertions(+), 23 deletions(-) diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 5fd904fd4..f0c770a5a 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -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 &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); diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 5e0a54df6..38270d938 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -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 m_Shaders; map m_Programs; map m_Pipelines; + vector< pair > 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; diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 188ee0591..8f2767c9d 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -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 GLReplay::PixelHistory(uint32_t frameID, vector events, ResourceId target, uint32_t x, uint32_t y, uint32_t sampleIdx) { RDCUNIMPLEMENTED("GLReplay::PixelHistory"); diff --git a/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs b/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs index eee27f8b0..2bb22e9b4 100644 --- a/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs +++ b/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs @@ -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(); + 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 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)