mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-15 22:40:50 +00:00
Refactor shader editing to more reliable approach
* When we replace a shader in GL there are a few knock-ons: we need to replace the programs that use this shader, and then from there we need to replace the pipelines that use the program. We also need to beware of programs created with glCreateShaderProgramv which refer to themselves as both a program and a shader. * Previously we'd look at the edited shader, then recurse and look at programs, then recurse and look at pipelines. We'd try to remember which one replaced which so we could undo it again. * Now we just do this in subsequent passes since there is only a one-way dependency: First replace the shader as needed, then update any programs and either replace or remove replacement as needed, and finally update any pipelines. * On Vulkan and D3D12 it's simpler as we just have shaders -> pipelines but the same principle applies.
This commit is contained in:
@@ -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<ResourceId, WrappedID3D12Resource1 *>();
|
||||
WrappedID3D12PipelineState::m_List = new std::vector<WrappedID3D12PipelineState *>();
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
|
||||
@@ -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<WrappedID3D12Shader>(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<ID3D12PipelineState *> 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<ID3D12PipelineState>(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<WrappedID3D12Shader>(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<WrappedID3D12Shader>(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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
GPUAddressRangeTracker WrappedID3D12Resource1::m_Addresses;
|
||||
std::map<ResourceId, WrappedID3D12Resource1 *> *WrappedID3D12Resource1::m_List = NULL;
|
||||
std::vector<WrappedID3D12PipelineState *> *WrappedID3D12PipelineState::m_List = NULL;
|
||||
std::map<WrappedID3D12PipelineState::DXBCKey, WrappedID3D12Shader *> WrappedID3D12Shader::m_Shaders;
|
||||
bool WrappedID3D12Shader::m_InternalResources = false;
|
||||
|
||||
|
||||
@@ -632,6 +632,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<WrappedID3D12PipelineState *> *m_List;
|
||||
|
||||
static std::vector<WrappedID3D12PipelineState *> &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<WrappedID3D12PipelineState *> 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)
|
||||
|
||||
+306
-208
@@ -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<GLint, GLint> 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<GLint, GLint> 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<ResourceId> 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<GLuint> 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<GLint, GLint> 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))
|
||||
|
||||
@@ -264,8 +264,6 @@ private:
|
||||
|
||||
std::map<ResourceId, BufferData> m_Buffers;
|
||||
|
||||
std::vector<rdcpair<ResourceId, Replacement>> 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
|
||||
{
|
||||
|
||||
@@ -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<VkPipeline> deletequeue;
|
||||
|
||||
VkShaderModule srcShaderModule = rm->GetCurrentHandle<VkShaderModule>(liveid);
|
||||
VkShaderModule dstShaderModule = rm->GetCurrentHandle<VkShaderModule>(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<VkPipeline>(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<VkShaderModule>(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<VkShaderModule>(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<VkPipeline>(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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user