From 7ab9815aaf2d76af01d4fb1afafe698799684623 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 13 Aug 2014 15:56:55 +0100 Subject: [PATCH] Introduce a bindpoint mapping as part of the pipeline state * For APIs where the shader namespace/bindpoint (which may be arbitrary like 'the Nth texture resource' can be mapped, at each event, to the actual API bind point where the object is. * On D3D11 this is pass-through, on GL this returns the value of each uniform. * This also means GL shader reflection structures are properly immutable and the variance in the uniform values is handled elsewhere. * In future this might need to be expanded to support more complex binding methods, where the mapping returns the resource rather than just mapping to an integer bind ponit. --- renderdoc/core/replay_proxy.cpp | 3 +- renderdoc/driver/d3d11/d3d11_common.cpp | 3 +- renderdoc/driver/d3d11/d3d11_replay.cpp | 15 ++ renderdoc/driver/gl/gl_replay.cpp | 166 ++++++++++++------ renderdoc/driver/gl/gl_replay.h | 2 + renderdoc/driver/gl/gl_shader_refl.cpp | 12 +- .../driver/gl/wrappers/gl_shader_funcs.cpp | 25 ++- renderdoc/replay/d3d11_pipestate.h | 1 + renderdoc/replay/gl_pipestate.h | 1 + renderdoc/replay/shader_types.h | 15 +- renderdocui/Code/CommonPipelineState.cs | 62 +++++-- renderdocui/Interop/D3D11PipelineState.cs | 2 + renderdocui/Interop/GLPipelineState.cs | 2 + renderdocui/Interop/Shader.cs | 19 +- .../PipelineState/GLPipelineStateViewer.cs | 42 +++-- renderdocui/Windows/TextureViewer.cs | 5 +- 16 files changed, 274 insertions(+), 101 deletions(-) diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 3e5e48746..44dbd3705 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -384,7 +384,7 @@ void Serialiser::Serialise(const char *name, ConstantBlock &el) { Serialise("", el.name); Serialise("", el.variables); - Serialise("", el.bufferAddress); + Serialise("", el.bufferBacked); Serialise("", el.bindPoint); } @@ -397,7 +397,6 @@ void Serialiser::Serialise(const char *name, ShaderResource &el) Serialise("", el.IsUAV); Serialise("", el.name); Serialise("", el.variableType); - Serialise("", el.variableAddress); Serialise("", el.bindPoint); } diff --git a/renderdoc/driver/d3d11/d3d11_common.cpp b/renderdoc/driver/d3d11/d3d11_common.cpp index 7274ec7ea..bfba2a32d 100644 --- a/renderdoc/driver/d3d11/d3d11_common.cpp +++ b/renderdoc/driver/d3d11/d3d11_common.cpp @@ -772,7 +772,7 @@ ShaderReflection *MakeShaderReflection(DXBC::DXBCFile *dxbc) { ConstantBlock &cb = ret->ConstantBlocks[i]; cb.name = dxbc->m_CBuffers[i].name; - cb.bufferAddress = (int32_t)i; + cb.bufferBacked = true; cb.bindPoint = (uint32_t)i; create_array_uninit(cb.variables, dxbc->m_CBuffers[i].variables.size()); @@ -798,7 +798,6 @@ ShaderReflection *MakeShaderReflection(DXBC::DXBCFile *dxbc) continue; ShaderResource res; - res.variableAddress = r.bindPoint; // N/A for D3D, bindPoint is address res.bindPoint = r.bindPoint; res.name = r.name; diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index c81389343..a5fb637b1 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -486,6 +486,21 @@ D3D11PipelineState D3D11Replay::MakePipelineState() dst.Shader = rm->GetOriginalID(id); dst.ShaderDetails = NULL; + // create identity bindpoint mapping + create_array_uninit(dst.BindpointMapping.ConstantBlocks, D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT); + for(int s=0; s < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; s++) + { + dst.BindpointMapping.ConstantBlocks[s].bind = s; + dst.BindpointMapping.ConstantBlocks[s].used = true; + } + + create_array_uninit(dst.BindpointMapping.Resources, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT); + for(int32_t s=0; s < D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT; s++) + { + dst.BindpointMapping.Resources[s].bind = s; + dst.BindpointMapping.Resources[s].used = true; + } + create_array_uninit(dst.ConstantBuffers, D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT); for(size_t s=0; s < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; s++) { diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 1f29c0ec9..815bd45e4 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -581,14 +581,11 @@ ShaderReflection *GLReplay::GetShader(ResourceId id) return NULL; } - // TODO shader reflection struct shouldn't be tied to the current program - // This is only really needed to fill the latest sampler uniform values, - // which should be saved in a mapping structure - GLuint curProg = 0; - gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); - - auto &refl = shaderDetails.reflection; + return &shaderDetails.reflection; +} +void GLReplay::GetMapping(WrappedOpenGL &gl, GLuint curProg, int shadIdx, ShaderReflection *refl, ShaderBindpointMapping &mapping) +{ // in case of bugs, we readback into this array instead of GLint dummyReadback[32]; @@ -597,30 +594,73 @@ ShaderReflection *GLReplay::GetShader(ResourceId id) dummyReadback[i] = 0x6c7b8a9d; #endif - // update with latest uniform values - for(int32_t i=0; i < refl.Resources.count; i++) + GLenum refEnum[] = { + eGL_REFERENCED_BY_VERTEX_SHADER, + eGL_REFERENCED_BY_TESS_CONTROL_SHADER, + eGL_REFERENCED_BY_TESS_EVALUATION_SHADER, + eGL_REFERENCED_BY_GEOMETRY_SHADER, + eGL_REFERENCED_BY_FRAGMENT_SHADER, + eGL_REFERENCED_BY_COMPUTE_SHADER, + }; + + create_array_uninit(mapping.Resources, refl->Resources.count); + for(int32_t i=0; i < refl->Resources.count; i++) { - if(refl.Resources.elems[i].IsSRV && refl.Resources.elems[i].IsTexture) + if(refl->Resources.elems[i].IsSRV && refl->Resources.elems[i].IsTexture) { - GLint loc = gl.glGetUniformLocation(curProg, refl.Resources.elems[i].name.elems); + GLint loc = gl.glGetUniformLocation(curProg, refl->Resources.elems[i].name.elems); if(loc >= 0) { gl.glGetUniformiv(curProg, loc, dummyReadback); - refl.Resources.elems[i].bindPoint = dummyReadback[0]; + mapping.Resources[i].bind = dummyReadback[0]; } } - } - for(int32_t i=0; i < refl.ConstantBlocks.count; i++) - { - if(refl.ConstantBlocks.elems[i].bufferAddress >= 0) + else { - GLint loc = gl.glGetUniformBlockIndex(curProg, refl.ConstantBlocks.elems[i].name.elems); + mapping.Resources[i].bind = -1; + } + + GLuint idx = gl.glGetProgramResourceIndex(curProg, eGL_UNIFORM, refl->Resources.elems[i].name.elems); + if(idx == GL_INVALID_INDEX) + { + mapping.Resources[i].used = false; + } + else + { + GLint used = 0; + gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, idx, 1, &refEnum[shadIdx], 1, NULL, &used); + mapping.Resources[i].used = (used != 0); + } + } + + create_array_uninit(mapping.ConstantBlocks, refl->ConstantBlocks.count); + for(int32_t i=0; i < refl->ConstantBlocks.count; i++) + { + if(refl->ConstantBlocks.elems[i].bufferBacked) + { + GLint loc = gl.glGetUniformBlockIndex(curProg, refl->ConstantBlocks.elems[i].name.elems); if(loc >= 0) { gl.glGetActiveUniformBlockiv(curProg, loc, eGL_UNIFORM_BLOCK_BINDING, dummyReadback); - refl.ConstantBlocks.elems[i].bindPoint = dummyReadback[0]; + mapping.ConstantBlocks[i].bind = dummyReadback[0]; } } + else + { + mapping.ConstantBlocks[i].bind = -1; + } + + GLuint idx = gl.glGetProgramResourceIndex(curProg, eGL_UNIFORM_BLOCK, refl->ConstantBlocks.elems[i].name.elems); + if(idx == GL_INVALID_INDEX) + { + mapping.ConstantBlocks[i].used = false; + } + else + { + GLint used = 0; + gl.glGetProgramResourceiv(curProg, eGL_UNIFORM_BLOCK, idx, 1, &refEnum[shadIdx], 1, NULL, &used); + mapping.ConstantBlocks[i].used = (used != 0); + } } #if !defined(RELEASE) @@ -628,8 +668,6 @@ ShaderReflection *GLReplay::GetShader(ResourceId id) if(dummyReadback[i] != 0x6c7b8a9d) RDCERR("Invalid uniform readback - data beyond first element modified!"); #endif - - return &refl; } void GLReplay::SavePipelineState() @@ -853,7 +891,29 @@ void GLReplay::SavePipelineState() GLuint curProg = 0; gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); + GLPipelineState::ShaderStage *stages[6] = { + &pipe.m_VS, + &pipe.m_TCS, + &pipe.m_TES, + &pipe.m_GS, + &pipe.m_FS, + &pipe.m_CS, + }; ShaderReflection *refls[6] = { NULL }; + ShaderBindpointMapping *mappings[6] = { NULL }; + + struct + { + GLenum bit; + GLenum type; + } shaders[] = { + { eGL_VERTEX_SHADER_BIT, eGL_VERTEX_SHADER }, + { eGL_TESS_CONTROL_SHADER_BIT, eGL_TESS_CONTROL_SHADER }, + { eGL_TESS_EVALUATION_SHADER_BIT, eGL_TESS_EVALUATION_SHADER }, + { eGL_GEOMETRY_SHADER_BIT, eGL_GEOMETRY_SHADER }, + { eGL_FRAGMENT_SHADER_BIT, eGL_FRAGMENT_SHADER }, + { eGL_COMPUTE_SHADER_BIT, eGL_COMPUTE_SHADER }, + }; if(curProg == 0) { @@ -877,46 +937,36 @@ void GLReplay::SavePipelineState() RDCASSERT(pipeDetails.programs.size()); - struct - { - GLenum bit; - GLenum type; - } shaders[] = { - { eGL_VERTEX_SHADER_BIT, eGL_VERTEX_SHADER }, - { eGL_FRAGMENT_SHADER_BIT, eGL_FRAGMENT_SHADER }, - { eGL_COMPUTE_SHADER_BIT, eGL_COMPUTE_SHADER }, - { eGL_GEOMETRY_SHADER_BIT, eGL_GEOMETRY_SHADER }, - { eGL_TESS_CONTROL_SHADER_BIT, eGL_TESS_CONTROL_SHADER }, - { eGL_TESS_EVALUATION_SHADER_BIT, eGL_TESS_EVALUATION_SHADER }, - }; + // TODO: we could pre-flatten this (to list all the shaders in the pipeline) + // look at the programs in the pipeline for(size_t p=0; p < pipeDetails.programs.size(); p++) { auto &progDetails = m_pDriver->m_Programs[pipeDetails.programs[p].id]; - + RDCASSERT(progDetails.shaders.size()); - for(size_t s=0; s < ARRAY_COUNT(shaders); s++) + curProg = rm->GetCurrentResource(pipeDetails.programs[p].id).name; + + // look at the shaders in the program + for(int s=0; s < ARRAY_COUNT(shaders); s++) { + // if this program is being used for a shader stage if(pipeDetails.programs[p].use & shaders[s].bit) { auto &progDetails = m_pDriver->m_Programs[pipeDetails.programs[p].id]; + // find the shader stage that's being used for(size_t i=0; i < progDetails.shaders.size(); i++) { if(m_pDriver->m_Shaders[ progDetails.shaders[i] ].type == shaders[s].type) { - ResourceId shid = rm->GetOriginalID(progDetails.shaders[i]); - switch(shaders[s].type) - { - case eGL_VERTEX_SHADER: pipe.m_VS.Shader = shid; break; - case eGL_FRAGMENT_SHADER: pipe.m_FS.Shader = shid; break; - case eGL_COMPUTE_SHADER: pipe.m_CS.Shader = shid; break; - case eGL_GEOMETRY_SHADER: pipe.m_GS.Shader = shid; break; - case eGL_TESS_CONTROL_SHADER: pipe.m_TCS.Shader = shid; break; - case eGL_TESS_EVALUATION_SHADER: pipe.m_TES.Shader = shid; break; - } + // set Shader ID and bindpoint mapping directly in the pipe state + // store reflection and mapping locally too + stages[s]->Shader = rm->GetOriginalID(progDetails.shaders[i]); refls[s] = GetShader(progDetails.shaders[i]); + GetMapping(gl, curProg, s, refls[s], stages[s]->BindpointMapping); + mappings[s] = &stages[s]->BindpointMapping; break; } } @@ -931,17 +981,23 @@ void GLReplay::SavePipelineState() RDCASSERT(progDetails.shaders.size()); + // look at the shaders in the program for(size_t i=0; i < progDetails.shaders.size(); i++) { - if(m_pDriver->m_Shaders[ progDetails.shaders[i] ].type == eGL_VERTEX_SHADER) - pipe.m_VS.Shader = rm->GetOriginalID(progDetails.shaders[i]); - else if(m_pDriver->m_Shaders[ progDetails.shaders[i] ].type == eGL_FRAGMENT_SHADER) - pipe.m_FS.Shader = rm->GetOriginalID(progDetails.shaders[i]); + // find the matching stage + for(int s=0; s < ARRAY_COUNT(shaders); s++) + { + if(m_pDriver->m_Shaders[ progDetails.shaders[i] ].type == shaders[s].type) + { + // set Shader ID and bindpoint mapping directly in the pipe state + // store reflection and mapping locally too + stages[s]->Shader = rm->GetOriginalID(progDetails.shaders[i]); + refls[s] = GetShader(progDetails.shaders[i]); + GetMapping(gl, curProg, s, refls[s], stages[s]->BindpointMapping); + mappings[s] = &stages[s]->BindpointMapping; + } + } } - - // prefetch uniform values in GetShader() - for(size_t s=0; s < progDetails.shaders.size(); s++) - refls[s] = GetShader(progDetails.shaders[s]); } // GL is ass-backwards in its handling of texture units. When a shader is active @@ -971,7 +1027,7 @@ void GLReplay::SavePipelineState() for(int32_t r=0; r < refls[s]->Resources.count; r++) { // bindPoint is the uniform value for this sampler - if(refls[s]->Resources[r].bindPoint == unit) + if(mappings[s]->Resources[ refls[s]->Resources[r].bindPoint ].bind == unit) { GLenum t = eGL_UNKNOWN_ENUM; @@ -1188,8 +1244,8 @@ void GLReplay::FillCBufferVariables(ResourceId shader, uint32_t cbufSlot, vector RDCEraseEl(var.value); - bool bufferBacked = (cblock.bindPoint >= 0 && cblock.bufferAddress >= 0 && !data.empty()); - bool hasValue = bufferBacked || (cblock.bindPoint < 0 && cblock.bufferAddress < 0); // buffer backed (with data), or global uniforms + bool bufferBacked = (cblock.bufferBacked && !data.empty()); + bool hasValue = bufferBacked || !cblock.bufferBacked; // buffer backed (with data), or global uniforms if(desc.elements == 1) { diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 6e1213905..5abc00572 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -134,6 +134,8 @@ class GLReplay : public IReplayDriver void FillCBufferValue(WrappedOpenGL &gl, GLuint prog, bool bufferBacked, bool rowMajor, uint32_t locA, uint32_t locB, const vector &data, ShaderVariable &outVar); + void GetMapping(WrappedOpenGL &gl, GLuint curProg, int shadIdx, ShaderReflection *refl, ShaderBindpointMapping &mapping); + struct OutputWindow : public GLWindowingData { struct diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index 499699d12..73e99db73 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -135,7 +135,7 @@ void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, res.variableType.descriptor.cols = 4; res.variableType.descriptor.elements = 1; res.variableType.descriptor.rowMajorStorage = false; - res.bindPoint = 0; + res.bindPoint = (int32_t)resources.size(); // float samplers if(values[0] == GL_SAMPLER_BUFFER) @@ -380,8 +380,6 @@ void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, continue; } - res.variableAddress = values[2]; - create_array_uninit(res.name, values[1]); gl.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[1], NULL, res.name.elems); res.name.count--; // trim off trailing null @@ -549,8 +547,8 @@ void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, ConstantBlock cblock; cblock.name = uboNames[i]; - cblock.bufferAddress = i; - cblock.bindPoint = -1; + cblock.bufferBacked = true; + cblock.bindPoint = (int32_t)cbuffers.size(); std::sort(ubos[i].begin(), ubos[i].end(), ubo_offset_sort()); cblock.variables = ubos[i]; @@ -564,8 +562,8 @@ void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, { ConstantBlock globals; globals.name = "$Globals"; - globals.bufferAddress = -1; - globals.bindPoint = -1; + globals.bufferBacked = false; + globals.bindPoint = (int32_t)cbuffers.size(); globals.variables = globalUniforms; cbuffers.push_back(globals); diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index 3cd92073c..ae9689354 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -321,12 +321,25 @@ bool WrappedOpenGL::Serialise_glCreateShaderProgramv(GLuint program, GLenum type GLResource res = ProgramRes(GetCtx(), real); ResourceId liveId = m_ResourceManager->RegisterResource(res); - - m_Programs[liveId].linked = true; - m_Programs[liveId].shaders.push_back(liveId); - m_Shaders[liveId].type = Type; - m_Shaders[liveId].sources.swap(src); - m_Shaders[liveId].prog = real; + + auto &progDetails = m_Programs[liveId]; + + progDetails.linked = true; + progDetails.shaders.push_back(liveId); + + auto &shadDetails = m_Shaders[liveId]; + + shadDetails.type = Type; + shadDetails.sources.swap(src); + shadDetails.prog = real; + MakeShaderReflection(m_Real, Type, real, shadDetails.reflection); + + create_array_uninit(shadDetails.reflection.DebugInfo.files, shadDetails.sources.size()); + for(size_t i=0; i < shadDetails.sources.size(); i++) + { + shadDetails.reflection.DebugInfo.files[i].first = StringFormat::Fmt("source%u.glsl", (uint32_t)i); + shadDetails.reflection.DebugInfo.files[i].second = shadDetails.sources[i]; + } GetResourceManager()->AddLiveResource(id, res); } diff --git a/renderdoc/replay/d3d11_pipestate.h b/renderdoc/replay/d3d11_pipestate.h index f608c985f..6fbc56bc0 100644 --- a/renderdoc/replay/d3d11_pipestate.h +++ b/renderdoc/replay/d3d11_pipestate.h @@ -72,6 +72,7 @@ struct D3D11PipelineState ShaderStage() : Shader(), ShaderDetails(NULL) {} ResourceId Shader; ShaderReflection *ShaderDetails; + ShaderBindpointMapping BindpointMapping; ShaderStageType stage; diff --git a/renderdoc/replay/gl_pipestate.h b/renderdoc/replay/gl_pipestate.h index 9ff03d24e..9939dc46e 100644 --- a/renderdoc/replay/gl_pipestate.h +++ b/renderdoc/replay/gl_pipestate.h @@ -69,6 +69,7 @@ struct GLPipelineState ShaderStage() : Shader(), ShaderDetails(NULL) {} ResourceId Shader; ShaderReflection *ShaderDetails; + ShaderBindpointMapping BindpointMapping; ShaderStageType stage; } m_VS, m_TCS, m_TES, m_GS, m_FS, m_CS; diff --git a/renderdoc/replay/shader_types.h b/renderdoc/replay/shader_types.h index 797938af6..da8ede0d0 100644 --- a/renderdoc/replay/shader_types.h +++ b/renderdoc/replay/shader_types.h @@ -164,7 +164,7 @@ struct ConstantBlock { rdctype::str name; rdctype::array variables; - int32_t bufferAddress; + bool32 bufferBacked; int32_t bindPoint; }; @@ -179,7 +179,6 @@ struct ShaderResource rdctype::str name; ShaderVariableType variableType; - int32_t variableAddress; int32_t bindPoint; }; @@ -209,3 +208,15 @@ struct ShaderReflection // TODO expand this to encompass shader subroutines. rdctype::array Interfaces; }; + +struct BindpointMap +{ + int32_t bind; + bool32 used; +}; + +struct ShaderBindpointMapping +{ + rdctype::array ConstantBlocks; + rdctype::array Resources; +}; diff --git a/renderdocui/Code/CommonPipelineState.cs b/renderdocui/Code/CommonPipelineState.cs index 2b2461cc7..1f2df71f5 100644 --- a/renderdocui/Code/CommonPipelineState.cs +++ b/renderdocui/Code/CommonPipelineState.cs @@ -109,6 +109,39 @@ namespace renderdocui.Code // there's a lot of redundancy in these functions + public ShaderBindpointMapping GetBindpointMapping(ShaderStageType stage) + { + if (LogLoaded) + { + if (IsLogD3D11) + { + switch (stage) + { + case ShaderStageType.Vertex: return m_D3D11.m_VS.BindpointMapping; + case ShaderStageType.Domain: return m_D3D11.m_DS.BindpointMapping; + case ShaderStageType.Hull: return m_D3D11.m_HS.BindpointMapping; + case ShaderStageType.Geometry: return m_D3D11.m_GS.BindpointMapping; + case ShaderStageType.Pixel: return m_D3D11.m_PS.BindpointMapping; + case ShaderStageType.Compute: return m_D3D11.m_CS.BindpointMapping; + } + } + else if (IsLogGL) + { + switch (stage) + { + case ShaderStageType.Vertex: return m_GL.m_VS.BindpointMapping; + case ShaderStageType.Tess_Control: return m_GL.m_TCS.BindpointMapping; + case ShaderStageType.Tess_Eval: return m_GL.m_TES.BindpointMapping; + case ShaderStageType.Geometry: return m_GL.m_GS.BindpointMapping; + case ShaderStageType.Fragment: return m_GL.m_FS.BindpointMapping; + case ShaderStageType.Compute: return m_GL.m_CS.BindpointMapping; + } + } + } + + return null; + } + public ShaderReflection GetShaderReflection(ShaderStageType stage) { if (LogLoaded) @@ -329,7 +362,7 @@ namespace renderdocui.Code return null; } - public void GetConstantBuffer(ShaderStageType stage, uint BindPoint, out ResourceId buf, out uint ByteOffset, out uint ByteSize) + public void GetConstantBuffer(ShaderStageType stage, uint BufIdx, out ResourceId buf, out uint ByteOffset, out uint ByteSize) { if (LogLoaded) { @@ -347,11 +380,11 @@ namespace renderdocui.Code case ShaderStageType.Compute: s = m_D3D11.m_CS; break; } - if(BindPoint < s.ConstantBuffers.Length) + if(BufIdx < s.ConstantBuffers.Length) { - buf = s.ConstantBuffers[BindPoint].Buffer; - ByteOffset = s.ConstantBuffers[BindPoint].VecOffset * 4 * sizeof(float); - ByteSize = s.ConstantBuffers[BindPoint].VecCount * 4 * sizeof(float); + buf = s.ConstantBuffers[BufIdx].Buffer; + ByteOffset = s.ConstantBuffers[BufIdx].VecOffset * 4 * sizeof(float); + ByteSize = s.ConstantBuffers[BufIdx].VecCount * 4 * sizeof(float); return; } @@ -370,18 +403,21 @@ namespace renderdocui.Code case ShaderStageType.Compute: s = m_GL.m_CS; break; } - if(s.ShaderDetails != null && BindPoint < s.ShaderDetails.ConstantBlocks.Length) + if(s.ShaderDetails != null && BufIdx < s.ShaderDetails.ConstantBlocks.Length) { - if(s.ShaderDetails.ConstantBlocks[BindPoint].bindPoint >= 0 && - s.ShaderDetails.ConstantBlocks[BindPoint].bindPoint < m_GL.UniformBuffers.Length) + if (s.ShaderDetails.ConstantBlocks[BufIdx].bindPoint >= 0) { - var b = m_GL.UniformBuffers[s.ShaderDetails.ConstantBlocks[BindPoint].bindPoint]; + int uboIdx = s.BindpointMapping.ConstantBlocks[s.ShaderDetails.ConstantBlocks[BufIdx].bindPoint].bind; + if (uboIdx >= 0 && uboIdx < m_GL.UniformBuffers.Length) + { + var b = m_GL.UniformBuffers[uboIdx]; - buf = b.Resource; - ByteOffset = (uint)b.Offset; - ByteSize = (uint)b.Size; + buf = b.Resource; + ByteOffset = (uint)b.Offset; + ByteSize = (uint)b.Size; - return; + return; + } } } } diff --git a/renderdocui/Interop/D3D11PipelineState.cs b/renderdocui/Interop/D3D11PipelineState.cs index 196446a83..619d390ca 100644 --- a/renderdocui/Interop/D3D11PipelineState.cs +++ b/renderdocui/Interop/D3D11PipelineState.cs @@ -106,6 +106,8 @@ namespace renderdoc private IntPtr _ptr_ShaderDetails; [CustomMarshalAs(CustomUnmanagedType.Skip)] public ShaderReflection ShaderDetails; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public ShaderBindpointMapping BindpointMapping; public ShaderStageType stage; diff --git a/renderdocui/Interop/GLPipelineState.cs b/renderdocui/Interop/GLPipelineState.cs index d6ba28be5..b8cdde6a8 100644 --- a/renderdocui/Interop/GLPipelineState.cs +++ b/renderdocui/Interop/GLPipelineState.cs @@ -90,6 +90,8 @@ namespace renderdoc private IntPtr _ptr_ShaderDetails; [CustomMarshalAs(CustomUnmanagedType.Skip)] public ShaderReflection ShaderDetails; + [CustomMarshalAs(CustomUnmanagedType.CustomClass)] + public ShaderBindpointMapping BindpointMapping; public ShaderStageType stage; }; diff --git a/renderdocui/Interop/Shader.cs b/renderdocui/Interop/Shader.cs index ac04550a6..ceeca0b49 100644 --- a/renderdocui/Interop/Shader.cs +++ b/renderdocui/Interop/Shader.cs @@ -342,7 +342,7 @@ namespace renderdoc [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] public ShaderConstant[] variables; - public Int32 bufferAddress; + public bool bufferBacked; public Int32 bindPoint; }; @@ -360,7 +360,6 @@ namespace renderdoc public string name; [CustomMarshalAs(CustomUnmanagedType.CustomClass)] public ShaderVariableType variableType; - public Int32 variableAddress; public Int32 bindPoint; }; @@ -414,4 +413,20 @@ namespace renderdoc [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] public Interface[] Interfaces; }; + + [StructLayout(LayoutKind.Sequential)] + public class BindpointMap + { + public Int32 bind; + public bool used; + }; + + [StructLayout(LayoutKind.Sequential)] + public class ShaderBindpointMapping + { + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public BindpointMap[] ConstantBlocks; + [CustomMarshalAs(CustomUnmanagedType.TemplatedArray)] + public BindpointMap[] Resources; + }; }; \ No newline at end of file diff --git a/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs b/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs index 9406c49d8..d25edfaec 100644 --- a/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs +++ b/renderdocui/Windows/PipelineState/GLPipelineStateViewer.cs @@ -174,7 +174,7 @@ namespace renderdocui.Windows.PipelineState // Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, - GLPipelineState.ShaderStage stage, + GLPipelineState state, GLPipelineState.ShaderStage stage, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView samplers, TreelistView.TreeListView cbuffers, TreelistView.TreeListView classes) { @@ -196,17 +196,34 @@ namespace renderdocui.Windows.PipelineState UInt32 i = 0; foreach (var shaderCBuf in shaderDetails.ConstantBlocks) { + int bindPoint = stage.BindpointMapping.ConstantBlocks[i].bind; + + bool filledSlot = !shaderCBuf.bufferBacked || + (bindPoint >= 0 && bindPoint < state.UniformBuffers.Length && state.UniformBuffers[bindPoint].Resource != ResourceId.Null); + bool usedSlot = stage.BindpointMapping.ConstantBlocks[i].used; + + // show if + if (usedSlot || // it's referenced by the shader - regardless of empty or not + (showDisabled.Checked && !usedSlot && filledSlot) || // it's bound, but not referenced, and we have "show disabled" + (showEmpty.Checked && !filledSlot) // it's empty, and we have "show empty" + ) { string name = shaderCBuf.name; int numvars = shaderCBuf.variables.Length; string slotname = i.ToString(); - var node = cbuffers.Nodes.Add(new object[] { slotname, name, "", "", numvars, "" }); + var node = cbuffers.Nodes.Add(new object[] { slotname, name, bindPoint, "", numvars, "" }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = i; + + if (!filledSlot) + EmptyRow(node); + + if (!usedSlot) + InactiveRow(node); } i++; } @@ -461,35 +478,40 @@ namespace renderdocui.Windows.PipelineState iabuffers.NodesSelection.Clear(); iabuffers.EndUpdate(); - SetShaderState(texs, bufs, state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsClasses); - SetShaderState(texs, bufs, state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsClasses); - SetShaderState(texs, bufs, state.m_TES, tesShader, tesResources, tesSamplers, tesCBuffers, tesClasses); - SetShaderState(texs, bufs, state.m_TCS, tcsShader, tcsResources, tcsSamplers, tcsCBuffers, tcsClasses); - SetShaderState(texs, bufs, state.m_FS, fsShader, fsResources, fsSamplers, fsCBuffers, fsClasses); - SetShaderState(texs, bufs, state.m_CS, csShader, csResources, csSamplers, csCBuffers, csClasses); + SetShaderState(texs, bufs, state, state.m_VS, vsShader, vsResources, vsSamplers, vsCBuffers, vsClasses); + SetShaderState(texs, bufs, state, state.m_GS, gsShader, gsResources, gsSamplers, gsCBuffers, gsClasses); + SetShaderState(texs, bufs, state, state.m_TES, tesShader, tesResources, tesSamplers, tesCBuffers, tesClasses); + SetShaderState(texs, bufs, state, state.m_TCS, tcsShader, tcsResources, tcsSamplers, tcsCBuffers, tcsClasses); + SetShaderState(texs, bufs, state, state.m_FS, fsShader, fsResources, fsSamplers, fsCBuffers, fsClasses); + SetShaderState(texs, bufs, state, state.m_CS, csShader, csResources, csSamplers, csCBuffers, csClasses); fsResources.BeginUpdate(); fsResources.Nodes.Clear(); if (state.Textures != null) { var shaderDetails = state.m_FS.ShaderDetails; + var mapping = state.m_FS.BindpointMapping; int i = 0; foreach (var r in state.Textures) { ShaderResource shaderInput = null; + BindpointMap map = null; if (shaderDetails != null) { foreach (var bind in shaderDetails.Resources) { - if (bind.IsSRV && bind.bindPoint == i) + if (bind.IsSRV && mapping.Resources[bind.bindPoint].bind == i) + { shaderInput = bind; + map = mapping.Resources[bind.bindPoint]; + } } } bool filledSlot = (r.Resource != ResourceId.Null); - bool usedSlot = (shaderInput != null); + bool usedSlot = (shaderInput != null && map.used); // show if if (usedSlot || // it's referenced by the shader - regardless of empty or not diff --git a/renderdocui/Windows/TextureViewer.cs b/renderdocui/Windows/TextureViewer.cs index 7179c43e8..7e51699b1 100644 --- a/renderdocui/Windows/TextureViewer.cs +++ b/renderdocui/Windows/TextureViewer.cs @@ -796,6 +796,7 @@ namespace renderdocui.Windows ResourceId[] Texs = m_Core.CurPipelineState.GetResources(ShaderStageType.Pixel); ShaderReflection details = m_Core.CurPipelineState.GetShaderReflection(ShaderStageType.Pixel); + ShaderBindpointMapping mapping = m_Core.CurPipelineState.GetBindpointMapping(ShaderStageType.Pixel); uint firstuav = uint.MaxValue; @@ -865,7 +866,7 @@ namespace renderdocui.Windows { foreach (var bind in details.Resources) { - if (bind.bindPoint == i && bind.IsUAV) + if (mapping.Resources[bind.bindPoint].bind == i && bind.IsUAV) { bindName = "<" + bind.name + ">"; } @@ -939,7 +940,7 @@ namespace renderdocui.Windows { foreach (var bind in details.Resources) { - if (bind.bindPoint == i && bind.IsSRV) + if (mapping.Resources[bind.bindPoint].bind == i && bind.IsSRV) { used = true; bindName = "<" + bind.name + ">";