diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index f89bdf4cd..91ab4e99d 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -243,12 +243,18 @@ WrappedID3D12Device::WrappedID3D12Device(ID3D12Device *realDevice, D3D12InitPara m_InitParams = params; + WrappedID3D12Resource1::m_List = NULL; + WrappedID3D12PipelineState::m_List = NULL; + if(RenderDoc::Inst().IsReplayApp()) { m_State = CaptureState::LoadingReplaying; if(realDevice) + { WrappedID3D12Resource1::m_List = new std::map(); + WrappedID3D12PipelineState::m_List = new std::vector(); + } m_FrameCaptureRecord = NULL; @@ -258,8 +264,6 @@ WrappedID3D12Device::WrappedID3D12Device(ID3D12Device *realDevice, D3D12InitPara { m_State = CaptureState::BackgroundCapturing; - WrappedID3D12Resource1::m_List = NULL; - if(m_pDevice) { typedef HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY)(REFIID, void **); @@ -421,7 +425,10 @@ WrappedID3D12Device::~WrappedID3D12Device() SAFE_RELEASE(m_InternalCmds.freecmds[i]); if(!IsStructuredExporting(m_State)) + { SAFE_DELETE(WrappedID3D12Resource1::m_List); + SAFE_DELETE(WrappedID3D12PipelineState::m_List); + } for(size_t i = 0; i < m_QueueFences.size(); i++) { diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 4166f716e..6cc645274 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -2849,106 +2849,134 @@ void D3D12Replay::BuildTargetShader(ShaderEncoding sourceEncoding, bytebuf sourc void D3D12Replay::ReplaceResource(ResourceId from, ResourceId to) { - D3D12ResourceManager *rm = m_pDevice->GetResourceManager(); + // replace the shader module + m_pDevice->GetResourceManager()->ReplaceResource(from, to); - // remove any previous replacement - RemoveReplacement(from); - - if(rm->HasLiveResource(from)) - { - ID3D12DeviceChild *resource = rm->GetLiveResource(from); - - if(WrappedID3D12Shader::IsAlloc(resource)) - { - WrappedID3D12Shader *sh = (WrappedID3D12Shader *)resource; - - for(size_t i = 0; i < sh->m_Pipes.size(); i++) - { - WrappedID3D12PipelineState *pipe = sh->m_Pipes[i]; - - ResourceId id = rm->GetOriginalID(pipe->GetResourceID()); - - ID3D12PipelineState *replpipe = NULL; - - D3D12_SHADER_BYTECODE shDesc = rm->GetLiveAs(to)->GetDesc(); - - if(pipe->graphics) - { - D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC desc = *pipe->graphics; - - D3D12_SHADER_BYTECODE *shaders[] = { - &desc.VS, &desc.HS, &desc.DS, &desc.GS, &desc.PS, - }; - - for(size_t s = 0; s < ARRAY_COUNT(shaders); s++) - { - if(shaders[s]->BytecodeLength > 0) - { - WrappedID3D12Shader *stage = (WrappedID3D12Shader *)shaders[s]->pShaderBytecode; - - if(stage->GetResourceID() == from) - *shaders[s] = shDesc; - else - *shaders[s] = stage->GetDesc(); - } - } - - m_pDevice->CreatePipeState(desc, &replpipe); - } - else - { - D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC desc = *pipe->compute; - - // replace the shader - desc.CS = shDesc; - - m_pDevice->CreatePipeState(desc, &replpipe); - } - - rm->ReplaceResource(id, GetResID(replpipe)); - } - } - - rm->ReplaceResource(from, to); - } + // now update any derived resources + RefreshDerivedReplacements(); ClearPostVSCache(); } void D3D12Replay::RemoveReplacement(ResourceId id) +{ + if(m_pDevice->GetResourceManager()->HasReplacement(id)) + { + m_pDevice->GetResourceManager()->RemoveReplacement(id); + + RefreshDerivedReplacements(); + + ClearPostVSCache(); + } +} + +void D3D12Replay::RefreshDerivedReplacements() { D3D12ResourceManager *rm = m_pDevice->GetResourceManager(); - rm->RemoveReplacement(id); + // we defer deletes of old replaced resources since it will invalidate elements in the vector + // we're iterating + std::vector deletequeue; - if(rm->HasLiveResource(id)) + for(WrappedID3D12PipelineState *pipe : WrappedID3D12PipelineState::GetList()) { - ID3D12DeviceChild *resource = rm->GetLiveResource(id); + ResourceId pipesrcid = pipe->GetResourceID(); + ResourceId origsrcid = rm->GetOriginalID(pipesrcid); - if(WrappedID3D12Shader::IsAlloc(resource)) + // only look at pipelines from the capture, no replay-time programs. + if(origsrcid == pipesrcid) + continue; + + // if this pipeline has a replacement, remove it and delete the program generated for it + if(rm->HasReplacement(origsrcid)) { - WrappedID3D12Shader *sh = (WrappedID3D12Shader *)resource; + deletequeue.push_back(rm->GetLiveAs(origsrcid)); - for(size_t i = 0; i < sh->m_Pipes.size(); i++) + rm->RemoveReplacement(origsrcid); + } + + bool usesReplacedShader = false; + + if(pipe->IsGraphics()) + { + ResourceId shaders[5]; + + if(pipe->VS()) + shaders[0] = rm->GetOriginalID(pipe->VS()->GetResourceID()); + if(pipe->HS()) + shaders[1] = rm->GetOriginalID(pipe->HS()->GetResourceID()); + if(pipe->DS()) + shaders[2] = rm->GetOriginalID(pipe->DS()->GetResourceID()); + if(pipe->GS()) + shaders[3] = rm->GetOriginalID(pipe->GS()->GetResourceID()); + if(pipe->PS()) + shaders[4] = rm->GetOriginalID(pipe->PS()->GetResourceID()); + + for(size_t i = 0; i < ARRAY_COUNT(shaders); i++) { - WrappedID3D12PipelineState *pipe = sh->m_Pipes[i]; - - ResourceId pipeid = rm->GetOriginalID(pipe->GetResourceID()); - - if(rm->HasReplacement(pipeid)) - { - // if there was an active replacement, remove the dependent replaced pipelines. - ID3D12DeviceChild *replpipe = rm->GetLiveResource(pipeid); - - rm->RemoveReplacement(pipeid); - - SAFE_RELEASE(replpipe); - } + usesReplacedShader = rm->HasReplacement(shaders[i]); + if(usesReplacedShader) + break; } } + else + { + if(rm->HasReplacement(rm->GetOriginalID(pipe->CS()->GetResourceID()))) + { + usesReplacedShader = true; + } + } + + // if there are replaced shaders in use, create a new pipeline with any/all replaced shaders. + if(usesReplacedShader) + { + ID3D12PipelineState *newpipe = NULL; + + if(pipe->IsGraphics()) + { + D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC desc = *pipe->graphics; + + D3D12_SHADER_BYTECODE *shaders[] = { + &desc.VS, &desc.HS, &desc.DS, &desc.GS, &desc.PS, + }; + + for(size_t s = 0; s < ARRAY_COUNT(shaders); s++) + { + if(shaders[s]->BytecodeLength > 0) + { + WrappedID3D12Shader *stage = (WrappedID3D12Shader *)shaders[s]->pShaderBytecode; + + // remap through the original ID to pick up any replacements + stage = rm->GetLiveAs(rm->GetOriginalID(stage->GetResourceID())); + + *shaders[s] = stage->GetDesc(); + } + } + + m_pDevice->CreatePipeState(desc, &newpipe); + } + else + { + D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC desc = *pipe->compute; + + WrappedID3D12Shader *stage = pipe->CS(); + + // remap through the original ID to pick up any replacements + stage = rm->GetLiveAs(rm->GetOriginalID(stage->GetResourceID())); + + desc.CS = stage->GetDesc(); + + m_pDevice->CreatePipeState(desc, &newpipe); + } + + rm->ReplaceResource(origsrcid, GetResID(newpipe)); + } } - ClearPostVSCache(); + for(ID3D12PipelineState *pipe : deletequeue) + { + SAFE_RELEASE(pipe); + } } void D3D12Replay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 63386b935..df3a39e5f 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -223,6 +223,8 @@ private: bool CreateSOBuffers(); + void RefreshDerivedReplacements(); + void BuildShader(ShaderEncoding sourceEncoding, bytebuf source, const std::string &entry, const ShaderCompileFlags &compileFlags, ShaderStage type, ResourceId *id, std::string *errors); diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index d2bc2449d..7901cc835 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -30,6 +30,7 @@ GPUAddressRangeTracker WrappedID3D12Resource1::m_Addresses; std::map *WrappedID3D12Resource1::m_List = NULL; +std::vector *WrappedID3D12PipelineState::m_List = NULL; std::map WrappedID3D12Shader::m_Shaders; bool WrappedID3D12Shader::m_InternalResources = false; diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index f0717636c..9846afbb8 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -632,6 +632,9 @@ public: } } + static std::vector *m_List; + + static std::vector &GetList() { return *m_List; } bool IsGraphics() { return graphics != NULL; } bool IsCompute() { return compute != NULL; } struct DXBCKey @@ -724,10 +727,6 @@ public: else shader->AddRef(); - if(pipeline && - std::find(shader->m_Pipes.begin(), shader->m_Pipes.end(), pipeline) == shader->m_Pipes.end()) - shader->m_Pipes.push_back(pipeline); - return shader; } @@ -779,8 +778,6 @@ public: return m_Mapping; } - std::vector m_Pipes; - private: ShaderEntry(const ShaderEntry &e); void TryReplaceOriginalByteCode(); @@ -817,9 +814,14 @@ public: WrappedID3D12PipelineState(ID3D12PipelineState *real, WrappedID3D12Device *device) : WrappedDeviceChild12(real, device) { + if(m_List) + m_List->push_back(this); } virtual ~WrappedID3D12PipelineState() { + if(m_List) + m_List->erase(std::find(m_List->begin(), m_List->end(), this)); + Shutdown(); if(graphics) diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 44845c4a8..1154a3c39 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -1577,235 +1577,130 @@ struct ReplacementSearch 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; - - PerStageReflections stages; - FillReflectionArray(it->first, stages); - - // 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()) - CopyProgramAttribBindings(progsrc, progdst, &m_Shaders[vs].reflection); - - if(fs != ResourceId()) - CopyProgramFragDataBindings(progsrc, progdst, &m_Shaders[fs].reflection); - - // 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 - { - PerStageReflections dstStages; - FillReflectionArray(progdstid, dstStages); - - std::map translate; - - // copy uniforms and set up new location translation table - CopyProgramUniforms(stages, progsrc, dstStages, progdst, &translate); - - // start with the original location translation table, to account for any - // capture-replay translation - m_Programs[progdstid].locationTranslate = m_Programs[progsrcid].locationTranslate; - - // compose on the one from editing. - for(auto lit = m_Programs[progdstid].locationTranslate.begin(); - lit != m_Programs[progdstid].locationTranslate.end(); lit++) - { - auto lit2 = translate.find(lit->second); - if(lit2 != translate.end()) - lit->second = lit2->second; - else - lit->second = -1; - } - - ResourceId origsrcid = GetResourceManager()->GetOriginalID(progsrcid); - - // recursively call to replaceresource (different type - these are programs) - ReplaceResource(origsrcid, progdstid); - - // insert into m_DependentReplacements - auto insertPos = - std::lower_bound(m_DependentReplacements.begin(), m_DependentReplacements.end(), - from, ReplacementSearch()); - m_DependentReplacements.insert( - insertPos, - make_rdcpair(from, Replacement(origsrcid, ProgramRes(GetCtx(), progdst)))); - } - - break; - } - } - } - } - - if(resource.Namespace == eResProgram) - { - // need to replace all pipelines that use this program - for(auto it = m_Pipelines.begin(); it != m_Pipelines.end(); ++it) - { - ResourceId pipesrcid = it->first; - PipelineData &pipedata = it->second; - - // see if the program is used - for(int i = 0; i < 6; i++) - { - if(pipedata.stagePrograms[i] == livefrom) - { - // make a new pipeline - GLuint pipedst = 0; - glGenProgramPipelines(1, &pipedst); - - ResourceId pipedstid = GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), pipedst)); - - // attach all but the i'th program - for(int j = 0; j < 6; j++) - { - if(i != j && pipedata.stagePrograms[j] != ResourceId()) - { - // if this stage was provided by the program we're replacing, use that instead - if(pipedata.stagePrograms[i] == pipedata.stagePrograms[j]) - glUseProgramStages(pipedst, ShaderBit(j), resource.name); - else - glUseProgramStages( - pipedst, ShaderBit(j), - GetResourceManager()->GetCurrentResource(pipedata.stagePrograms[j]).name); - } - } - - // attach the new program in our stage - glUseProgramStages(pipedst, ShaderBit(i), resource.name); - - ResourceId origsrcid = GetResourceManager()->GetOriginalID(pipesrcid); - - // recursively call to replaceresource (different type - these are programs) - ReplaceResource(origsrcid, pipedstid); - - // insert into m_DependentReplacements - auto insertPos = - std::lower_bound(m_DependentReplacements.begin(), m_DependentReplacements.end(), - from, ReplacementSearch()); - m_DependentReplacements.insert( - insertPos, - make_rdcpair(from, Replacement(origsrcid, ProgramPipeRes(GetCtx(), pipedst)))); - } - } - } - } + GLResource fromresource = GetResourceManager()->GetLiveResource(from); + GLResource toresource = GetResourceManager()->GetLiveResource(to); // do actual replacement - GLResource fromresource = GetResourceManager()->GetLiveResource(from); - // if they're the same type it's easy, but it could be we want to replace a shader - // inside a program which never had a shader (ie. glCreateShaderProgramv) - if(fromresource.Namespace == resource.Namespace) + if(fromresource.Namespace == toresource.Namespace) { + GetResourceManager()->RemoveReplacement(from); + + // if they're the same type we can just replace directly GetResourceManager()->ReplaceResource(from, to); } - else if(fromresource.Namespace == eResProgram && resource.Namespace == eResShader) + else if(fromresource.Namespace == eResProgram && toresource.Namespace == eResShader) { - // if we want to replace a program with a shader, assume it's just a program with only one - // shader attached. This will have been handled above in the "programs dependent on this - // shader", so we can just skip doing anything here + // if we want to replace a program with a shader, this is a glCreateShaderProgramv so we need + // to handle it specially + + ResourceId progsrcid = GetResourceManager()->GetLiveID(from); + const ProgramData &progdata = m_Programs[progsrcid]; + + // if this program has a replacement, remove it and delete the program generated for it + if(GetResourceManager()->HasReplacement(from)) + { + glDeleteProgram(GetResourceManager()->GetLiveResource(from).name); + GetResourceManager()->RemoveReplacement(from); + } + + GLuint progsrc = GetResourceManager()->GetLiveResource(from).name; + + // make a new program + GLuint progdst = glCreateProgram(); + + ResourceId progdstid = GetResourceManager()->GetID(ProgramRes(GetCtx(), progdst)); + + // attach the shader + glAttachShader(progdst, GetResourceManager()->GetCurrentResource(to).name); + + // mark separable + glProgramParameteri(progdst, eGL_PROGRAM_SEPARABLE, GL_TRUE); + + // copy VS or FS bindings as necessary + ResourceId vs = progdata.stageShaders[0]; + ResourceId fs = progdata.stageShaders[4]; + + if(vs != ResourceId()) + CopyProgramAttribBindings(progsrc, progdst, &m_Shaders[vs].reflection); + + if(fs != ResourceId()) + CopyProgramFragDataBindings(progsrc, progdst, &m_Shaders[fs].reflection); + + // 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 glCreateShaderProgramv shader, program failed " + "to link. Skipping replacement:\n%s", + buffer); + + delete[] buffer; + + glDeleteProgram(progdst); + } + else + { + PerStageReflections dstStages; + FillReflectionArray(progdstid, dstStages); + + std::map translate; + + PerStageReflections stages; + FillReflectionArray(progsrcid, stages); + + // copy uniforms and set up new location translation table + CopyProgramUniforms(stages, progsrc, dstStages, progdst, &translate); + + // start with the original location translation table, to account for any + // capture-replay translation + m_Programs[progdstid].locationTranslate = m_Programs[progsrcid].locationTranslate; + + // compose on the one from editing. + for(auto lit = m_Programs[progdstid].locationTranslate.begin(); + lit != m_Programs[progdstid].locationTranslate.end(); lit++) + { + auto lit2 = translate.find(lit->second); + if(lit2 != translate.end()) + lit->second = lit2->second; + else + lit->second = -1; + } + + // replace the program + GetResourceManager()->ReplaceResource(from, progdstid); + } } else { RDCERR("Unsupported replacement type from type %d to type %d", fromresource.Namespace, - resource.Namespace); + toresource.Namespace); } + + RefreshDerivedReplacements(); } } void WrappedOpenGL::RemoveReplacement(ResourceId id) { - // do actual removal - GetResourceManager()->RemoveReplacement(id); - - std::set recurse; - - // 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();) + if(GetResourceManager()->HasReplacement(id)) { - GetResourceManager()->RemoveReplacement(it->second.id); - recurse.insert(it->second.id); + GetResourceManager()->RemoveReplacement(id); - switch(it->second.res.Namespace) - { - case eResProgram: glDeleteProgram(it->second.res.name); break; - case eResProgramPipe: glDeleteProgramPipelines(1, &it->second.res.name); break; - default: RDCERR("Unexpected resource type to be freed"); break; - } - - it = m_DependentReplacements.erase(it); - } - - for(auto recurseit = recurse.begin(); recurseit != recurse.end(); ++recurseit) - { - // recursive call in case there are any dependents on this resource - RemoveReplacement(*recurseit); + RefreshDerivedReplacements(); } } @@ -1825,6 +1720,209 @@ void WrappedOpenGL::FreeTargetResource(ResourceId id) } } +void WrappedOpenGL::RefreshDerivedReplacements() +{ + // we defer deletes of old replaced resources since it will invalidate elements in the vector + // we're iterating + std::vector deletequeue; + + // first go through programs and replace any that need to be updated based on whether they have + // any replaced shaders + for(auto it = m_Programs.begin(); it != m_Programs.end(); ++it) + { + ResourceId progsrcid = it->first; + const ProgramData &progdata = it->second; + + ResourceId origsrcid = GetResourceManager()->GetOriginalID(progsrcid); + + // only look at programs from the capture, no replay-time programs. + if(origsrcid == progsrcid) + continue; + + // skip glCreateShaderProgramv programs. We handled this above and we don't want to try and + // create a dependent program or remove the replacement + if(progdata.shaderProgramUnlinkable) + continue; + + // if this program has a replacement, remove it and delete the program generated for it + if(GetResourceManager()->HasReplacement(origsrcid)) + { + deletequeue.push_back(GetResourceManager()->GetLiveResource(origsrcid).name); + GetResourceManager()->RemoveReplacement(origsrcid); + } + + bool usesReplacedShader = false; + + for(int i = 0; i < 6; i++) + { + if(GetResourceManager()->HasReplacement( + GetResourceManager()->GetOriginalID(progdata.stageShaders[i]))) + { + usesReplacedShader = true; + break; + } + } + + // if there are replaced shaders in use, create a new program with any/all replaced shaders. + if(usesReplacedShader) + { + GLuint progsrc = GetResourceManager()->GetCurrentResource(progsrcid).name; + + // make a new program + GLuint progdst = glCreateProgram(); + + ResourceId progdstid = GetResourceManager()->GetID(ProgramRes(GetCtx(), progdst)); + + // attach shaders, going via the original ID to pick up replacements + for(int i = 0; i < 6; i++) + { + if(progdata.stageShaders[i] != ResourceId()) + { + ResourceId shaderorigid = GetResourceManager()->GetOriginalID(progdata.stageShaders[i]); + glAttachShader(progdst, GetResourceManager()->GetLiveResource(shaderorigid).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()) + CopyProgramAttribBindings(progsrc, progdst, &m_Shaders[vs].reflection); + + if(fs != ResourceId()) + CopyProgramFragDataBindings(progsrc, progdst, &m_Shaders[fs].reflection); + + // 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 + { + PerStageReflections dstStages; + FillReflectionArray(progdstid, dstStages); + + std::map translate; + + PerStageReflections stages; + FillReflectionArray(progsrcid, stages); + + // copy uniforms and set up new location translation table + CopyProgramUniforms(stages, progsrc, dstStages, progdst, &translate); + + // start with the original location translation table, to account for any + // capture-replay translation + m_Programs[progdstid].locationTranslate = m_Programs[progsrcid].locationTranslate; + + // compose on the one from editing. + for(auto lit = m_Programs[progdstid].locationTranslate.begin(); + lit != m_Programs[progdstid].locationTranslate.end(); lit++) + { + auto lit2 = translate.find(lit->second); + if(lit2 != translate.end()) + lit->second = lit2->second; + else + lit->second = -1; + } + + // replace the program + GetResourceManager()->ReplaceResource(origsrcid, progdstid); + } + } + } + + for(GLuint prog : deletequeue) + glDeleteProgram(prog); + + deletequeue.clear(); + + // then go through pipelines based on replaced programs, as above + for(auto it = m_Pipelines.begin(); it != m_Pipelines.end(); ++it) + { + ResourceId pipesrcid = it->first; + const PipelineData &pipedata = it->second; + + ResourceId origsrcid = GetResourceManager()->GetOriginalID(pipesrcid); + + // only look at programs from the capture, no replay-time programs. + if(origsrcid == pipesrcid) + continue; + + // if this pipeline has a replacement, remove it and delete the pipeline generated for it + if(GetResourceManager()->HasReplacement(origsrcid)) + { + deletequeue.push_back(GetResourceManager()->GetLiveResource(origsrcid).name); + GetResourceManager()->RemoveReplacement(origsrcid); + } + + bool usesReplacedProgram = false; + + for(int i = 0; i < 6; i++) + { + if(GetResourceManager()->HasReplacement( + GetResourceManager()->GetOriginalID(pipedata.stagePrograms[i]))) + { + usesReplacedProgram = true; + break; + } + } + + // if there are replaced shaders in use, create a new program with any/all replaced shaders. + if(usesReplacedProgram) + { + // make a new pipeline + GLuint pipedst = 0; + glGenProgramPipelines(1, &pipedst); + + ResourceId pipedstid = GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), pipedst)); + + // attach programs, going via the original ID to pick up replacements + for(int i = 0; i < 6; i++) + { + if(pipedata.stagePrograms[i] != ResourceId()) + { + ResourceId progorigid = GetResourceManager()->GetOriginalID(pipedata.stagePrograms[i]); + glUseProgramStages(pipedst, ShaderBit(i), + GetResourceManager()->GetLiveResource(progorigid).name); + } + } + + // replace the pipeline + GetResourceManager()->ReplaceResource(origsrcid, pipedstid); + } + } + + for(GLuint prog : deletequeue) + glDeleteProgramPipelines(1, &prog); + + deletequeue.clear(); +} + void WrappedOpenGL::SwapBuffers(WindowingSystem winSystem, void *windowHandle) { if(IsBackgroundCapturing(m_State)) diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 015afc329..70d901c7f 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -264,8 +264,6 @@ private: std::map m_Buffers; - std::vector> m_DependentReplacements; - // this object is only created on old captures where VAO0 was a single global object. In new // captures each context has its own VAO0. GLuint m_Global_VAO0 = 0; @@ -477,6 +475,7 @@ private: void ReplaceResource(ResourceId from, ResourceId to); void RemoveReplacement(ResourceId id); void FreeTargetResource(ResourceId id); + void RefreshDerivedReplacements(); struct QueuedResource { diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index baf76d527..84771000f 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -3927,48 +3927,89 @@ void VulkanReplay::FreeTargetResource(ResourceId id) } void VulkanReplay::ReplaceResource(ResourceId from, ResourceId to) +{ + // replace the shader module + m_pDriver->GetResourceManager()->ReplaceResource(from, to); + + // now update any derived resources + RefreshDerivedReplacements(); + + ClearPostVSCache(); + ClearFeedbackCache(); +} + +void VulkanReplay::RemoveReplacement(ResourceId id) +{ + if(m_pDriver->GetResourceManager()->HasReplacement(id)) + { + m_pDriver->GetResourceManager()->RemoveReplacement(id); + + RefreshDerivedReplacements(); + + ClearPostVSCache(); + ClearFeedbackCache(); + } +} + +void VulkanReplay::RefreshDerivedReplacements() { VkDevice dev = m_pDriver->GetDev(); VulkanResourceManager *rm = m_pDriver->GetResourceManager(); - // we're passed in the original ID but we want the live ID for comparison - ResourceId liveid = rm->GetLiveID(from); + // we defer deletes of old replaced resources since it will invalidate elements in the vector + // we're iterating + std::vector deletequeue; - VkShaderModule srcShaderModule = rm->GetCurrentHandle(liveid); - VkShaderModule dstShaderModule = rm->GetCurrentHandle(to); - - // remake and replace any pipelines that referenced this shader + // remake and replace any pipelines that reference a replaced shader for(auto it = m_pDriver->m_CreationInfo.m_Pipeline.begin(); it != m_pDriver->m_CreationInfo.m_Pipeline.end(); ++it) { - bool refdShader = false; + ResourceId pipesrcid = it->first; + const VulkanCreationInfo::Pipeline &pipeInfo = it->second; + + ResourceId origsrcid = rm->GetOriginalID(pipesrcid); + + // only look at pipelines from the capture, no replay-time programs. + if(origsrcid == pipesrcid) + continue; + + // if this pipeline has a replacement, remove it and delete the program generated for it + if(rm->HasReplacement(origsrcid)) + { + deletequeue.push_back(rm->GetLiveHandle(origsrcid)); + + rm->RemoveReplacement(origsrcid); + } + + bool usesReplacedShader = false; for(size_t i = 0; i < ARRAY_COUNT(it->second.shaders); i++) { - if(it->second.shaders[i].module == liveid) + if(rm->HasReplacement(rm->GetOriginalID(it->second.shaders[i].module))) { - refdShader = true; + usesReplacedShader = true; break; } } - if(refdShader) + // if there are replaced shaders in use, create a new pipeline with any/all replaced shaders. + if(usesReplacedShader) { VkPipeline pipe = VK_NULL_HANDLE; - const VulkanCreationInfo::Pipeline &pipeInfo = m_pDriver->m_CreationInfo.m_Pipeline[it->first]; + if(pipeInfo.renderpass != ResourceId()) // check if this is a graphics or compute pipeline { VkGraphicsPipelineCreateInfo pipeCreateInfo; m_pDriver->GetShaderCache()->MakeGraphicsPipelineInfo(pipeCreateInfo, it->first); - // replace the relevant module + // replace the modules by going via the live ID to pick up any replacements for(uint32_t i = 0; i < pipeCreateInfo.stageCount; i++) { VkPipelineShaderStageCreateInfo &sh = (VkPipelineShaderStageCreateInfo &)pipeCreateInfo.pStages[i]; - if(sh.module == srcShaderModule) - sh.module = dstShaderModule; + ResourceId shadOrigId = rm->GetOriginalID(GetResID(sh.module)); + sh.module = rm->GetLiveHandle(shadOrigId); } // if we have pipeline executable properties, capture the data @@ -3988,10 +4029,10 @@ void VulkanReplay::ReplaceResource(ResourceId from, ResourceId to) VkComputePipelineCreateInfo pipeCreateInfo; m_pDriver->GetShaderCache()->MakeComputePipelineInfo(pipeCreateInfo, it->first); - // replace the relevant module + // replace the module by going via the live ID to pick up any replacements VkPipelineShaderStageCreateInfo &sh = pipeCreateInfo.stage; - RDCASSERT(sh.module == srcShaderModule); - sh.module = dstShaderModule; + ResourceId shadOrigId = rm->GetOriginalID(pipeInfo.shaders[5].module); + sh.module = rm->GetLiveHandle(shadOrigId); // if we have pipeline executable properties, capture the data if(m_pDriver->GetExtensions(NULL).ext_KHR_pipeline_executable_properties) @@ -4007,64 +4048,12 @@ void VulkanReplay::ReplaceResource(ResourceId from, ResourceId to) } // remove the replacements - rm->ReplaceResource(it->first, GetResID(pipe)); - rm->ReplaceResource(rm->GetOriginalID(it->first), GetResID(pipe)); + rm->ReplaceResource(origsrcid, GetResID(pipe)); } } - // make the actual shader module replacements - rm->ReplaceResource(from, to); - rm->ReplaceResource(liveid, to); - - ClearPostVSCache(); - ClearFeedbackCache(); -} - -void VulkanReplay::RemoveReplacement(ResourceId id) -{ - VkDevice dev = m_pDriver->GetDev(); - - VulkanResourceManager *rm = m_pDriver->GetResourceManager(); - - // we're passed in the original ID but we want the live ID for comparison - ResourceId liveid = rm->GetLiveID(id); - - if(!rm->HasReplacement(id)) - return; - - // remove the actual shader module replacements - rm->RemoveReplacement(id); - rm->RemoveReplacement(liveid); - - // remove any replacements on pipelines that referenced this shader - for(auto it = m_pDriver->m_CreationInfo.m_Pipeline.begin(); - it != m_pDriver->m_CreationInfo.m_Pipeline.end(); ++it) - { - bool refdShader = false; - for(size_t i = 0; i < ARRAY_COUNT(it->second.shaders); i++) - { - if(it->second.shaders[i].module == liveid) - { - refdShader = true; - break; - } - } - - if(refdShader) - { - VkPipeline pipe = rm->GetCurrentHandle(it->first); - - // delete the replacement pipeline - m_pDriver->vkDestroyPipeline(dev, pipe, NULL); - - // remove both live and original replacements, since we will have made these above - rm->RemoveReplacement(it->first); - rm->RemoveReplacement(rm->GetOriginalID(it->first)); - } - } - - ClearPostVSCache(); - ClearFeedbackCache(); + for(VkPipeline pipe : deletequeue) + m_pDriver->vkDestroyPipeline(dev, pipe, NULL); } ShaderDebugTrace VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 9918cfb0c..0cbc79f8a 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -422,6 +422,8 @@ private: void FetchTessGSOut(uint32_t eventId); void ClearPostVSCache(); + void RefreshDerivedReplacements(); + bool RenderTextureInternal(TextureDisplay cfg, VkRenderPassBeginInfo rpbegin, int flags); bool GetMinMax(ResourceId texid, uint32_t sliceFace, uint32_t mip, uint32_t sample,