diff --git a/renderdoc/driver/d3d11/d3d11_common.cpp b/renderdoc/driver/d3d11/d3d11_common.cpp index afbe464d2..62722edb0 100644 --- a/renderdoc/driver/d3d11/d3d11_common.cpp +++ b/renderdoc/driver/d3d11/d3d11_common.cpp @@ -963,7 +963,7 @@ ShaderReflection *MakeShaderReflection(DXBC::DXBCFile *dxbc) { ConstantBlock &cb = ret->ConstantBlocks[i]; cb.name = dxbc->m_CBuffers[i].name; - cb.bufferBacked = true; + cb.bufferBacked = dxbc->m_CBuffers[i].descriptor.type == DXBC::CBuffer::Descriptor::TYPE_CBUFFER; cb.bindPoint = (uint32_t)i; create_array_uninit(cb.variables, dxbc->m_CBuffers[i].variables.size()); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 4fd2c288d..582b7de0f 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -501,6 +501,13 @@ D3D11PipelineState D3D11Replay::MakePipelineState() ResourceId id = GetIDForResource(src.Shader); + WrappedShader *shad = (WrappedShader*)(WrappedID3D11Shader *)src.Shader; + + ShaderReflection *refl = NULL; + + if(shad != NULL) + refl = shad->GetDetails(); + dst.Shader = rm->GetOriginalID(id); dst.ShaderDetails = NULL; @@ -530,7 +537,7 @@ D3D11PipelineState D3D11Replay::MakePipelineState() { dst.BindpointMapping.ConstantBlocks[s].bindset = 0; dst.BindpointMapping.ConstantBlocks[s].bind = s; - dst.BindpointMapping.ConstantBlocks[s].used = true; + dst.BindpointMapping.ConstantBlocks[s].used = false; dst.BindpointMapping.ConstantBlocks[i].arraySize = 1; } @@ -539,7 +546,7 @@ D3D11PipelineState D3D11Replay::MakePipelineState() { dst.BindpointMapping.ReadOnlyResources[s].bindset = 0; dst.BindpointMapping.ReadOnlyResources[s].bind = s; - dst.BindpointMapping.ReadOnlyResources[s].used = true; + dst.BindpointMapping.ReadOnlyResources[s].used = false; dst.BindpointMapping.ReadOnlyResources[i].arraySize = 1; } @@ -548,10 +555,25 @@ D3D11PipelineState D3D11Replay::MakePipelineState() { dst.BindpointMapping.ReadWriteResources[s].bindset = 0; dst.BindpointMapping.ReadWriteResources[s].bind = s; - dst.BindpointMapping.ReadWriteResources[s].used = true; + dst.BindpointMapping.ReadWriteResources[s].used = false; dst.BindpointMapping.ReadWriteResources[i].arraySize = 1; } + // mark resources as used if they are referenced by the shader + if(refl) + { + for(int32_t i=0; i < refl->ConstantBlocks.count; i++) + if(refl->ConstantBlocks[i].bufferBacked) + dst.BindpointMapping.ConstantBlocks[refl->ConstantBlocks[i].bindPoint].used = true; + + for(int32_t i=0; i < refl->ReadOnlyResources.count; i++) + if(!refl->ReadOnlyResources[i].IsSampler) + dst.BindpointMapping.ReadOnlyResources[refl->ReadOnlyResources[i].bindPoint].used = true; + + for(int32_t i=0; i < refl->ReadWriteResources.count; i++) + dst.BindpointMapping.ReadWriteResources[refl->ReadWriteResources[i].bindPoint].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/shaders/spirv/spirv_disassemble.cpp b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp index cd6202d4e..ce25dde67 100644 --- a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp @@ -2142,6 +2142,14 @@ struct bindpair if(map.bindset != o.map.bindset) return map.bindset < o.map.bindset; + // sort -1 to the end + if(map.bind == -1 && o.map.bind == -1) // equal + return false; + if(map.bind == -1) // -1 not less than anything + return false; + if(o.map.bind == -1) // anything less than -1 + return true; + return map.bind < o.map.bind; } }; @@ -2271,6 +2279,10 @@ void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapp cblock.bufferBacked = true; BindpointMap bindmap = {0}; + // set can be implicitly 0, but the binding must be set explicitly. + // If no binding is found, we set -1 and sort to the end of the resources + // list as it's not bound anywhere (most likely, declared but not used) + bindmap.bind = -1; for(size_t d=0; d < inst->decorations.size(); d++) { @@ -2301,6 +2313,10 @@ void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapp } } } + + // should never have elements that have no binding declared but + // are used + RDCASSERT(!bindmap.used || bindmap.bind >= 0); cblocks.push_back(cblockpair(bindmap, cblock)); } @@ -2350,6 +2366,10 @@ void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapp res.variableType.descriptor.rowMajorStorage = false; BindpointMap bindmap = {0}; + // set can be implicitly 0, but the binding must be set explicitly. + // If no binding is found, we set -1 and sort to the end of the resources + // list as it's not bound anywhere (most likely, declared but not used) + bindmap.bind = -1; for(size_t d=0; d < inst->decorations.size(); d++) { @@ -2378,6 +2398,10 @@ void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapp } } + // should never have elements that have no binding declared but + // are used + RDCASSERT(!bindmap.used || bindmap.bind >= 0); + resources.push_back(shaderrespair(bindmap, res)); } } @@ -2402,6 +2426,11 @@ void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapp for(size_t i=0; i < cblocks.size(); i++) { mapping->ConstantBlocks[i] = cblocks[i].map; + // fix up any bind points marked with -1. They were sorted to the end + // but from here on we want to just be able to index with the bind point + // without any special casing. + if(mapping->ConstantBlocks[i].bind == -1) + mapping->ConstantBlocks[i].bind = 0; reflection->ConstantBlocks[i] = cblocks[i].bindres; reflection->ConstantBlocks[i].bindPoint = (int32_t)i; } @@ -2409,6 +2438,11 @@ void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapp for(size_t i=0; i < resources.size(); i++) { mapping->ReadOnlyResources[i] = resources[i].map; + // fix up any bind points marked with -1. They were sorted to the end + // but from here on we want to just be able to index with the bind point + // without any special casing. + if(mapping->ReadOnlyResources[i].bind == -1) + mapping->ReadOnlyResources[i].bind = 0; reflection->ReadOnlyResources[i] = resources[i].bindres; reflection->ReadOnlyResources[i].bindPoint = (int32_t)i; } diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 62088e6b5..c66bfb841 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -2927,24 +2927,48 @@ void VulkanReplay::SavePipelineState() layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT || layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { - ResourceId viewid = rm->GetNonDispWrapper(info[a].imageView)->id; + VkImageView view = info[a].imageView; - dst.bindings[b].binds[a].view = rm->GetOriginalID(viewid); - dst.bindings[b].binds[a].res = rm->GetOriginalID(c.m_ImageView[viewid].image); - dst.bindings[b].binds[a].baseMip = c.m_ImageView[viewid].range.baseMipLevel; - dst.bindings[b].binds[a].baseLayer = c.m_ImageView[viewid].range.baseArrayLayer; + if(view != VK_NULL_HANDLE) + { + ResourceId viewid = rm->GetNonDispWrapper(view)->id; + + dst.bindings[b].binds[a].view = rm->GetOriginalID(viewid); + dst.bindings[b].binds[a].res = rm->GetOriginalID(c.m_ImageView[viewid].image); + dst.bindings[b].binds[a].baseMip = c.m_ImageView[viewid].range.baseMipLevel; + dst.bindings[b].binds[a].baseLayer = c.m_ImageView[viewid].range.baseArrayLayer; + } + else + { + dst.bindings[b].binds[a].view = ResourceId(); + dst.bindings[b].binds[a].res = ResourceId(); + dst.bindings[b].binds[a].baseMip = 0; + dst.bindings[b].binds[a].baseLayer = 0; + } } if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER || layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) { - ResourceId viewid = rm->GetNonDispWrapper(info[a].bufferView)->id; + VkBufferView view = info[a].bufferView; + + if(view != VK_NULL_HANDLE) + { + ResourceId viewid = rm->GetNonDispWrapper(view)->id; - dst.bindings[b].binds[a].view = rm->GetOriginalID(viewid); - dst.bindings[b].binds[a].res = rm->GetOriginalID(c.m_BufferView[viewid].buffer); - dst.bindings[b].binds[a].offset = c.m_BufferView[viewid].offset; - if(dynamicOffset) - dst.bindings[b].binds[a].offset += *(uint32_t *)&info[a].imageLayout; - dst.bindings[b].binds[a].size = c.m_BufferView[viewid].size; + dst.bindings[b].binds[a].view = rm->GetOriginalID(viewid); + dst.bindings[b].binds[a].res = rm->GetOriginalID(c.m_BufferView[viewid].buffer); + dst.bindings[b].binds[a].offset = c.m_BufferView[viewid].offset; + if(dynamicOffset) + dst.bindings[b].binds[a].offset += *(uint32_t *)&info[a].imageLayout; + dst.bindings[b].binds[a].size = c.m_BufferView[viewid].size; + } + else + { + dst.bindings[b].binds[a].view = ResourceId(); + dst.bindings[b].binds[a].res = ResourceId(); + dst.bindings[b].binds[a].offset = 0; + dst.bindings[b].binds[a].size = 0; + } } if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC || @@ -2952,10 +2976,14 @@ void VulkanReplay::SavePipelineState() layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) { dst.bindings[b].binds[a].view = ResourceId(); - dst.bindings[b].binds[a].res = rm->GetOriginalID(rm->GetNonDispWrapper(info[a].bufferInfo.buffer)->id); + + if(info[a].bufferInfo.buffer != VK_NULL_HANDLE) + dst.bindings[b].binds[a].res = rm->GetOriginalID(rm->GetNonDispWrapper(info[a].bufferInfo.buffer)->id); + dst.bindings[b].binds[a].offset = info[a].bufferInfo.offset; if(dynamicOffset) dst.bindings[b].binds[a].offset += *(uint32_t *)&info[a].imageLayout; + dst.bindings[b].binds[a].size = info[a].bufferInfo.range; } } diff --git a/renderdocui/Code/CommonPipelineState.cs b/renderdocui/Code/CommonPipelineState.cs index 542b1c8fc..cd96b1a87 100644 --- a/renderdocui/Code/CommonPipelineState.cs +++ b/renderdocui/Code/CommonPipelineState.cs @@ -30,6 +30,37 @@ using renderdoc; namespace renderdocui.Code { + public class BoundResource + { + public BoundResource() + { Id = ResourceId.Null; HighestMip = -1; FirstSlice = -1; } + public BoundResource(ResourceId id) + { Id = id; HighestMip = -1; FirstSlice = -1; } + + public ResourceId Id; + public int HighestMip; + public int FirstSlice; + }; + + public struct BoundVBuffer + { + public ResourceId Buffer; + public ulong ByteOffset; + public uint ByteStride; + }; + + public struct VertexInputAttribute + { + public string Name; + public int VertexBuffer; + public uint RelativeByteOffset; + public bool PerInstance; + public int InstanceRate; + public ResourceFormat Format; + public object[] GenericValue; + public bool Used; + }; + public class CommonPipelineState { private D3D11PipelineState m_D3D11 = null; @@ -419,20 +450,13 @@ namespace renderdocui.Code return uint.MaxValue; } - public struct VBuffer - { - public ResourceId Buffer; - public ulong ByteOffset; - public uint ByteStride; - }; - - public VBuffer[] GetVBuffers() + public BoundVBuffer[] GetVBuffers() { if (LogLoaded) { if (IsLogD3D11) { - VBuffer[] ret = new VBuffer[m_D3D11.m_IA.vbuffers.Length]; + BoundVBuffer[] ret = new BoundVBuffer[m_D3D11.m_IA.vbuffers.Length]; for (int i = 0; i < m_D3D11.m_IA.vbuffers.Length; i++) { ret[i].Buffer = m_D3D11.m_IA.vbuffers[i].Buffer; @@ -444,7 +468,7 @@ namespace renderdocui.Code } else if (IsLogGL) { - VBuffer[] ret = new VBuffer[m_GL.m_VtxIn.vbuffers.Length]; + BoundVBuffer[] ret = new BoundVBuffer[m_GL.m_VtxIn.vbuffers.Length]; for (int i = 0; i < m_GL.m_VtxIn.vbuffers.Length; i++) { ret[i].Buffer = m_GL.m_VtxIn.vbuffers[i].Buffer; @@ -456,7 +480,7 @@ namespace renderdocui.Code } else if (IsLogVK) { - VBuffer[] ret = new VBuffer[m_Vulkan.VI.binds.Length]; + BoundVBuffer[] ret = new BoundVBuffer[m_Vulkan.VI.binds.Length]; for (int i = 0; i < m_Vulkan.VI.binds.Length; i++) { ret[i].Buffer = i < m_Vulkan.VI.vbuffers.Length ? m_Vulkan.VI.vbuffers[i].buffer : ResourceId.Null; @@ -471,18 +495,6 @@ namespace renderdocui.Code return null; } - public struct VertexInputAttribute - { - public string Name; - public int VertexBuffer; - public uint RelativeByteOffset; - public bool PerInstance; - public int InstanceRate; - public ResourceFormat Format; - public object[] GenericValue; - public bool Used; - }; - public VertexInputAttribute[] GetVertexInputs() { if (LogLoaded) @@ -759,8 +771,10 @@ namespace renderdocui.Code ByteSize = 0; } - public ResourceId[] GetResources(ShaderStageType stage) + public Dictionary GetReadOnlyResources(ShaderStageType stage) { + var ret = new Dictionary(); + if (LogLoaded) { if (IsLogD3D11) @@ -777,17 +791,33 @@ namespace renderdocui.Code case ShaderStageType.Compute: s = m_D3D11.m_CS; break; } - ResourceId[] ret = new ResourceId[s.SRVs.Length]; for (int i = 0; i < s.SRVs.Length; i++) - ret[i] = s.SRVs[i].Resource; + { + var key = new BindpointMap(0, i); + var val = new BoundResource(); + + val.Id = s.SRVs[i].Resource; + val.HighestMip = (int)s.SRVs[i].HighestMip; + val.FirstSlice = (int)s.SRVs[i].FirstArraySlice; + + ret.Add(key, new BoundResource[] { val }); + } return ret; } else if (IsLogGL) { - ResourceId[] ret = new ResourceId[m_GL.Textures.Length]; for (int i = 0; i < m_GL.Textures.Length; i++) - ret[i] = m_GL.Textures[i].Resource; + { + var key = new BindpointMap(0, i); + var val = new BoundResource(); + + val.Id = m_GL.Textures[i].Resource; + val.HighestMip = (int)m_GL.Textures[i].HighestMip; + val.FirstSlice = (int)m_GL.Textures[i].FirstSlice; + + ret.Add(key, new BoundResource[] { val }); + } return ret; } @@ -797,68 +827,96 @@ namespace renderdocui.Code if (stage == ShaderStageType.Compute) descsets = m_Vulkan.compute.DescSets; - - List ret = new List(); ShaderStageBits mask = (ShaderStageBits)(1 << (int)stage); - foreach(var descset in m_Vulkan.graphics.DescSets) + + for (int set = 0; set < m_Vulkan.graphics.DescSets.Length; set++) { - foreach (var bind in descset.bindings) + var descset = m_Vulkan.graphics.DescSets[set]; + for (int slot = 0; slot < descset.bindings.Length; slot++) { + var bind = descset.bindings[slot]; if ((bind.type == ShaderBindType.ImageSampler || bind.type == ShaderBindType.InputAttachment || - bind.type == ShaderBindType.ReadOnlyBuffer || bind.type == ShaderBindType.ReadOnlyImage || bind.type == ShaderBindType.ReadOnlyTBuffer ) && (bind.stageFlags & mask) == mask) { - // VKTODOMED handle bind.arraySize - ret.Add(bind.binds[0].res); - } - else - { - // texture viewer currently expects binds to match up to resource list - // so we need to pad with empty elements - ret.Add(ResourceId.Null); + var key = new BindpointMap(set, slot); + var val = new BoundResource[bind.arraySize]; + + for (UInt32 i = 0; i < bind.arraySize; i++) + { + val[i] = new BoundResource(); + val[i].Id = bind.binds[i].res; + val[i].HighestMip = (int)bind.binds[i].baseMip; + val[i].FirstSlice = (int)bind.binds[i].baseLayer; + } + + ret.Add(key, val); } } } - return ret.ToArray(); + return ret; } } - return new ResourceId[0]; + return ret; } - public ResourceId[] GetReadWriteResources(ShaderStageType stage) + public Dictionary GetReadWriteResources(ShaderStageType stage) { + var ret = new Dictionary(); + if (LogLoaded) { if (IsLogD3D11) { if (stage == ShaderStageType.Compute) { - ResourceId[] ret = new ResourceId[m_D3D11.m_CS.UAVs.Length]; for (int i = 0; i < m_D3D11.m_CS.UAVs.Length; i++) - ret[i] = m_D3D11.m_CS.UAVs[i].Resource; + { + var key = new BindpointMap(0, i); + var val = new BoundResource(); - return ret; + val.Id = m_D3D11.m_CS.UAVs[i].Resource; + val.HighestMip = (int)m_D3D11.m_CS.UAVs[i].HighestMip; + val.FirstSlice = (int)m_D3D11.m_CS.UAVs[i].FirstArraySlice; + + ret.Add(key, new BoundResource[] { val }); + } } else { - ResourceId[] ret = new ResourceId[m_D3D11.m_OM.UAVs.Length]; for (int i = 0; i < m_D3D11.m_OM.UAVs.Length; i++) - ret[i] = m_D3D11.m_OM.UAVs[i].Resource; + { + var key = new BindpointMap(0, i); + var val = new BoundResource(); - return ret; + val.Id = m_D3D11.m_OM.UAVs[i].Resource; + val.HighestMip = (int)m_D3D11.m_OM.UAVs[i].HighestMip; + val.FirstSlice = (int)m_D3D11.m_OM.UAVs[i].FirstArraySlice; + + ret.Add(key, new BoundResource[] { val }); + } } + + return ret; } else if (IsLogGL) { - ResourceId[] ret = new ResourceId[m_GL.Images.Length]; for (int i = 0; i < m_GL.Images.Length; i++) - ret[i] = m_GL.Images[i].Resource; + { + var key = new BindpointMap(0, i); + var val = new BoundResource(); + + val.Id = m_GL.Images[i].Resource; + val.HighestMip = (int)m_GL.Images[i].Level; + val.FirstSlice = (int)m_GL.Images[i].Layer; + + ret.Add(key, new BoundResource[] { val }); + } return ret; } @@ -869,109 +927,115 @@ namespace renderdocui.Code if (stage == ShaderStageType.Compute) descsets = m_Vulkan.compute.DescSets; - List ret = new List(); - ShaderStageBits mask = (ShaderStageBits)(1 << (int)stage); - foreach (var descset in m_Vulkan.graphics.DescSets) + for (int set = 0; set < m_Vulkan.graphics.DescSets.Length; set++) { - foreach (var bind in descset.bindings) + var descset = m_Vulkan.graphics.DescSets[set]; + for (int slot = 0; slot < descset.bindings.Length; slot++) { + var bind = descset.bindings[slot]; + if ((bind.type == ShaderBindType.ReadWriteBuffer || bind.type == ShaderBindType.ReadWriteImage || bind.type == ShaderBindType.ReadWriteTBuffer ) && (bind.stageFlags & mask) == mask) { - // VKTODOMED handle bind.arraySize - ret.Add(bind.binds[0].res); - } - else - { - // texture viewer currently expects binds to match up to resource list - // so we need to pad with empty elements - ret.Add(ResourceId.Null); + var key = new BindpointMap(set, slot); + var val = new BoundResource[bind.arraySize]; + + for (UInt32 i = 0; i < bind.arraySize; i++) + { + val[i] = new BoundResource(); + val[i].Id = bind.binds[i].res; + val[i].HighestMip = (int)bind.binds[i].baseMip; + val[i].FirstSlice = (int)bind.binds[i].baseLayer; + } + + ret.Add(key, val); } } } - return ret.ToArray(); + return ret; } } - return new ResourceId[0]; + return ret; } - public ResourceId GetDepthTarget() + public BoundResource GetDepthTarget() { if (LogLoaded) { if (IsLogD3D11) { - return m_D3D11.m_OM.DepthTarget.Resource; + var ret = new BoundResource(); + ret.Id = m_D3D11.m_OM.DepthTarget.Resource; + ret.HighestMip = (int)m_D3D11.m_OM.DepthTarget.HighestMip; + ret.FirstSlice = (int)m_D3D11.m_OM.DepthTarget.FirstArraySlice; + return ret; } else if (IsLogGL) { - return m_GL.m_FB.m_DrawFBO.Depth.Obj; + var ret = new BoundResource(); + ret.Id = m_GL.m_FB.m_DrawFBO.Depth.Obj; + ret.HighestMip = (int)m_GL.m_FB.m_DrawFBO.Depth.Mip; + ret.FirstSlice = (int)m_GL.m_FB.m_DrawFBO.Depth.Layer; + return ret; } else if (IsLogVK) { var rp = m_Vulkan.Pass.renderpass; var fb = m_Vulkan.Pass.framebuffer; - return rp.depthstencilAttachment >= 0 && rp.depthstencilAttachment < fb.attachments.Length - ? fb.attachments[rp.depthstencilAttachment].img - : ResourceId.Null; + if (rp.depthstencilAttachment >= 0 && rp.depthstencilAttachment < fb.attachments.Length) + { + var ret = new BoundResource(); + ret.Id = fb.attachments[rp.depthstencilAttachment].img; + ret.HighestMip = (int)fb.attachments[rp.depthstencilAttachment].baseMip; + ret.FirstSlice = (int)fb.attachments[rp.depthstencilAttachment].baseArray; + return ret; + } + + return new BoundResource(); } } - return ResourceId.Null; + return new BoundResource(); } - public ResourceId GetStencilTarget() + public BoundResource[] GetOutputTargets() { if (LogLoaded) { if (IsLogD3D11) { - return m_D3D11.m_OM.DepthTarget.Resource; - } - else if (IsLogGL) - { - return m_GL.m_FB.m_DrawFBO.Stencil.Obj; - } - else if (IsLogVK) - { - var rp = m_Vulkan.Pass.renderpass; - var fb = m_Vulkan.Pass.framebuffer; - - return rp.depthstencilAttachment >= 0 && rp.depthstencilAttachment < fb.attachments.Length - ? fb.attachments[rp.depthstencilAttachment].img - : ResourceId.Null; - } - } - - return ResourceId.Null; - } - - public ResourceId[] GetOutputTargets() - { - if (LogLoaded) - { - if (IsLogD3D11) - { - ResourceId[] ret = new ResourceId[m_D3D11.m_OM.RenderTargets.Length]; + BoundResource[] ret = new BoundResource[m_D3D11.m_OM.RenderTargets.Length]; for (int i = 0; i < m_D3D11.m_OM.RenderTargets.Length; i++) - ret[i] = m_D3D11.m_OM.RenderTargets[i].Resource; + { + ret[i] = new BoundResource(); + ret[i].Id = m_D3D11.m_OM.RenderTargets[i].Resource; + ret[i].HighestMip = (int)m_D3D11.m_OM.RenderTargets[i].HighestMip; + ret[i].FirstSlice = (int)m_D3D11.m_OM.RenderTargets[i].FirstArraySlice; + } return ret; } else if (IsLogGL) { - ResourceId[] ret = new ResourceId[m_GL.m_FB.m_DrawFBO.DrawBuffers.Length]; - for(int i=0; i < m_GL.m_FB.m_DrawFBO.DrawBuffers.Length; i++) + BoundResource[] ret = new BoundResource[m_GL.m_FB.m_DrawFBO.DrawBuffers.Length]; + for (int i = 0; i < m_GL.m_FB.m_DrawFBO.DrawBuffers.Length; i++) { + ret[i] = new BoundResource(); + int db = m_GL.m_FB.m_DrawFBO.DrawBuffers[i]; - if(db >= 0) - ret[i] = m_GL.m_FB.m_DrawFBO.Color[db].Obj; + + if (db >= 0) + { + ret[i].Id = m_GL.m_FB.m_DrawFBO.Color[db].Obj; + ret[i].HighestMip = (int)m_GL.m_FB.m_DrawFBO.Color[db].Mip; + ret[i].FirstSlice = (int)m_GL.m_FB.m_DrawFBO.Color[db].Layer; + } } return ret; @@ -980,17 +1044,25 @@ namespace renderdocui.Code { var rp = m_Vulkan.Pass.renderpass; var fb = m_Vulkan.Pass.framebuffer; - ResourceId[] ret = new ResourceId[rp.colorAttachments.Length]; + + BoundResource[] ret = new BoundResource[rp.colorAttachments.Length]; for (int i = 0; i < rp.colorAttachments.Length; i++) - ret[i] = rp.colorAttachments[i] < fb.attachments.Length - ? fb.attachments[rp.colorAttachments[i]].img - : ResourceId.Null; + { + ret[i] = new BoundResource(); + + if(rp.colorAttachments[i] < fb.attachments.Length) + { + ret[i].Id = fb.attachments[rp.colorAttachments[i]].img; + ret[i].HighestMip = (int)fb.attachments[rp.colorAttachments[i]].baseMip; + ret[i].FirstSlice = (int)fb.attachments[rp.colorAttachments[i]].baseArray; + } + } return ret; } } - return new ResourceId[0]; + return new BoundResource[0]; } // Still to add: diff --git a/renderdocui/Interop/Shader.cs b/renderdocui/Interop/Shader.cs index 0cac737a2..e10ec77cf 100644 --- a/renderdocui/Interop/Shader.cs +++ b/renderdocui/Interop/Shader.cs @@ -455,6 +455,38 @@ namespace renderdoc public Int32 bind; public bool used; public UInt32 arraySize; + + public BindpointMap() + { + } + + public BindpointMap(Int32 set, Int32 slot) + { + bindset = set; + bind = slot; + used = false; + arraySize = 1; + } + + public override bool Equals(Object obj) + { + return obj is BindpointMap && this == (BindpointMap)obj; + } + public override int GetHashCode() + { + int hash = bindset.GetHashCode() * 17; + hash = hash * 17 + bind.GetHashCode(); + return hash; + } + public static bool operator ==(BindpointMap x, BindpointMap y) + { + return x.bindset == y.bindset && + x.bind == y.bind; + } + public static bool operator !=(BindpointMap x, BindpointMap y) + { + return !(x == y); + } }; [StructLayout(LayoutKind.Sequential)] diff --git a/renderdocui/Windows/BufferViewer.cs b/renderdocui/Windows/BufferViewer.cs index a3429efcf..3423ae2f0 100644 --- a/renderdocui/Windows/BufferViewer.cs +++ b/renderdocui/Windows/BufferViewer.cs @@ -818,7 +818,7 @@ namespace renderdocui.Windows return ret; } - CommonPipelineState.VBuffer[] vbs = m_Core.CurPipelineState.GetVBuffers(); + BoundVBuffer[] vbs = m_Core.CurPipelineState.GetVBuffers(); ResourceId[] bs = new ResourceId[vbs.Length]; uint[] s = new uint[vbs.Length]; @@ -2678,19 +2678,19 @@ namespace renderdocui.Windows m_MeshDisplay.aspect = 1.0f; // take a guess for the aspect ratio, for if the user hasn't overridden it - ResourceId depth = m_Core.CurPipelineState.GetDepthTarget(); - ResourceId[] targets = m_Core.CurPipelineState.GetOutputTargets(); + BoundResource depth = m_Core.CurPipelineState.GetDepthTarget(); + BoundResource[] targets = m_Core.CurPipelineState.GetOutputTargets(); - if (depth != ResourceId.Null || (targets != null && targets.Length > 0)) + if (depth.Id != ResourceId.Null || (targets != null && targets.Length > 0)) { foreach (var t in m_Core.CurTextures) { - if (depth != ResourceId.Null && t.ID == depth) + if (depth.Id != ResourceId.Null && t.ID == depth.Id) { m_MeshDisplay.aspect = (float)t.width / (float)t.height; break; } - if (depth == ResourceId.Null && targets != null && targets.Length > 0 && t.ID == targets[0]) + if (depth.Id == ResourceId.Null && targets != null && targets.Length > 0 && t.ID == targets[0].Id) { m_MeshDisplay.aspect = (float)t.width / (float)t.height; break; diff --git a/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs b/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs index e86121c4a..9498d95a0 100644 --- a/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs +++ b/renderdocui/Windows/PipelineState/VulkanPipelineStateViewer.cs @@ -390,10 +390,12 @@ namespace renderdocui.Windows.PipelineState var descriptorBind = slotBinds[idx]; if (bindMap.arraySize > 1) + { slotname = String.Format("{0}: {1}[{2}]", bindMap.bind, shaderRes.name, idx); - if (shaderRes.name.Length > 0) - slotname += ": " + shaderRes.name; + if (shaderRes.name.Length > 0) + slotname += ": " + shaderRes.name; + } bool isbuf = false; UInt32 w = 1, h = 1, d = 1; diff --git a/renderdocui/Windows/TextureViewer.Designer.cs b/renderdocui/Windows/TextureViewer.Designer.cs index c70605839..46a06abe3 100644 --- a/renderdocui/Windows/TextureViewer.Designer.cs +++ b/renderdocui/Windows/TextureViewer.Designer.cs @@ -140,8 +140,8 @@ this.closeTab = new System.Windows.Forms.ToolStripMenuItem(); this.closeOtherTabs = new System.Windows.Forms.ToolStripMenuItem(); this.closeTabsToRight = new System.Windows.Forms.ToolStripMenuItem(); - this.texPanel = new renderdocui.Controls.ThumbnailStrip(); - this.rtPanel = new renderdocui.Controls.ThumbnailStrip(); + this.roPanel = new renderdocui.Controls.ThumbnailStrip(); + this.rwPanel = new renderdocui.Controls.ThumbnailStrip(); this.rangeHistogram = new renderdocui.Controls.RangeHistogram(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.pixelContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); @@ -1246,28 +1246,28 @@ // // texPanel // - this.texPanel.AutoScroll = true; - this.texPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.texPanel.Location = new System.Drawing.Point(461, 373); - this.texPanel.Margin = new System.Windows.Forms.Padding(0); - this.texPanel.MinimumSize = new System.Drawing.Size(2, 60); - this.texPanel.Name = "texPanel"; - this.texPanel.Padding = new System.Windows.Forms.Padding(3); - this.texPanel.Size = new System.Drawing.Size(200, 60); - this.texPanel.TabIndex = 3; - this.texPanel.MouseClick += new System.Windows.Forms.MouseEventHandler(this.thumbsLayout_MouseClick); + this.roPanel.AutoScroll = true; + this.roPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.roPanel.Location = new System.Drawing.Point(461, 373); + this.roPanel.Margin = new System.Windows.Forms.Padding(0); + this.roPanel.MinimumSize = new System.Drawing.Size(2, 60); + this.roPanel.Name = "texPanel"; + this.roPanel.Padding = new System.Windows.Forms.Padding(3); + this.roPanel.Size = new System.Drawing.Size(200, 60); + this.roPanel.TabIndex = 3; + this.roPanel.MouseClick += new System.Windows.Forms.MouseEventHandler(this.thumbsLayout_MouseClick); // // rtPanel // - this.rtPanel.AutoScroll = true; - this.rtPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.rtPanel.Location = new System.Drawing.Point(242, 373); - this.rtPanel.Margin = new System.Windows.Forms.Padding(0); - this.rtPanel.MinimumSize = new System.Drawing.Size(2, 60); - this.rtPanel.Name = "rtPanel"; - this.rtPanel.Size = new System.Drawing.Size(200, 60); - this.rtPanel.TabIndex = 2; - this.rtPanel.MouseClick += new System.Windows.Forms.MouseEventHandler(this.thumbsLayout_MouseClick); + this.rwPanel.AutoScroll = true; + this.rwPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.rwPanel.Location = new System.Drawing.Point(242, 373); + this.rwPanel.Margin = new System.Windows.Forms.Padding(0); + this.rwPanel.MinimumSize = new System.Drawing.Size(2, 60); + this.rwPanel.Name = "rtPanel"; + this.rwPanel.Size = new System.Drawing.Size(200, 60); + this.rwPanel.TabIndex = 2; + this.rwPanel.MouseClick += new System.Windows.Forms.MouseEventHandler(this.thumbsLayout_MouseClick); // // rangeHistogram // @@ -1310,8 +1310,8 @@ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1128, 492); this.Controls.Add(this.texlistContainer); - this.Controls.Add(this.texPanel); - this.Controls.Add(this.rtPanel); + this.Controls.Add(this.roPanel); + this.Controls.Add(this.rwPanel); this.Controls.Add(this.pixelContextPanel); this.Controls.Add(this.renderToolstripContainer); this.Controls.Add(this.rangeHistogram); @@ -1386,8 +1386,8 @@ private Controls.NoScrollPanel renderContainer; private Controls.NoScrollPanel render; private Controls.TextureListBox textureList; - private Controls.ThumbnailStrip rtPanel; - private Controls.ThumbnailStrip texPanel; + private Controls.ThumbnailStrip rwPanel; + private Controls.ThumbnailStrip roPanel; private System.Windows.Forms.ToolStrip zoomStrip; private System.Windows.Forms.ToolStripLabel toolStripLabel4; private System.Windows.Forms.ToolStripButton fitToWindow; diff --git a/renderdocui/Windows/TextureViewer.cs b/renderdocui/Windows/TextureViewer.cs index 9c2c5a4b9..c43bdf6ca 100644 --- a/renderdocui/Windows/TextureViewer.cs +++ b/renderdocui/Windows/TextureViewer.cs @@ -59,17 +59,23 @@ namespace renderdocui.Windows private int m_HighWaterStatusLength = 0; - public enum FollowType { OutputColour, OutputDepth, ReadWriteRes, InputResource } + public enum FollowType { OutputColour, OutputDepth, ReadWrite, ReadOnly } struct Following { public FollowType Type; + public ShaderStageType Stage; public int index; + public int arrayEl; - public Following(FollowType t, int i) { Type = t; index = i; } + public static Following Default = new Following(FollowType.OutputColour, ShaderStageType.Pixel, 0, 0); + + public Following(FollowType t, ShaderStageType s, int i, int a) { Type = t; Stage = s; index = i; arrayEl = a; } public override int GetHashCode() { - return Type.GetHashCode() + index.GetHashCode(); + return Type.GetHashCode() + + Stage.GetHashCode() + + index.GetHashCode(); } public override bool Equals(object obj) { @@ -77,15 +83,16 @@ namespace renderdocui.Windows } public static bool operator ==(Following s1, Following s2) { - return s1.Type == s2.Type && s1.index == s2.index; + return s1.Type == s2.Type && + s1.Stage == s2.Stage && + s1.index == s2.index; } public static bool operator !=(Following s1, Following s2) { return !(s1 == s2); } - // todo, implement these better for GL :( - private static void GetDrawContext(Core core, out bool copy, out bool compute) + public static void GetDrawContext(Core core, out bool copy, out bool compute) { var curDraw = core.CurDrawcall; copy = curDraw != null && (curDraw.flags & (DrawcallFlags.Copy | DrawcallFlags.Resolve)) != 0; @@ -99,57 +106,10 @@ namespace renderdocui.Windows bool copy, compute; GetDrawContext(core, out copy, out compute); - // TODO find a way (without copy-pasting tons of code) - // to duplicate the logic of GetResourceId() but return - // different information if (copy || compute) return -1; - // VKTODOLOW temp - need to tidy this up properly - if (core.APIProps.pipelineType == APIPipelineStateType.Vulkan) - return -1; - - if (core.APIProps.pipelineType == APIPipelineStateType.D3D11) - { - D3D11PipelineState.ShaderStage.ResourceView view = null; - - if (Type == FollowType.OutputColour) - { - view = core.CurD3D11PipelineState.m_OM.RenderTargets[index]; - } - else if (Type == FollowType.OutputDepth) - { - view = core.CurD3D11PipelineState.m_OM.DepthTarget; - } - else if (Type == FollowType.ReadWriteRes) - { - if (index >= core.CurD3D11PipelineState.m_OM.UAVStartSlot) - view = core.CurD3D11PipelineState.m_OM.UAVs[index]; - } - else if (Type == FollowType.InputResource) - { - view = core.CurD3D11PipelineState.m_PS.SRVs[index]; - } - - return view != null ? (int)view.HighestMip : -1; - } - else - { - if (Type == FollowType.OutputColour) - { - return (int)core.CurGLPipelineState.m_FB.m_DrawFBO.Color[index].Mip; - } - else if (Type == FollowType.OutputDepth) - { - return (int)core.CurGLPipelineState.m_FB.m_DrawFBO.Depth.Mip; - } - else if (Type == FollowType.ReadWriteRes) - { - return (int)core.CurGLPipelineState.Images[index].Level; - } - } - - return -1; + return GetBoundResource(core, arrayEl).HighestMip; } public int GetFirstArraySlice(Core core) @@ -158,121 +118,108 @@ namespace renderdocui.Windows bool copy, compute; GetDrawContext(core, out copy, out compute); - // TODO find a way (without copy-pasting tons of code) - // to duplicate the logic of GetResourceId() but return - // different information if (copy || compute) return -1; - // VKTODOLOW temp - need to tidy this up properly - if (core.APIProps.pipelineType == APIPipelineStateType.Vulkan) - return -1; - - if (core.APIProps.pipelineType == APIPipelineStateType.D3D11) - { - D3D11PipelineState.ShaderStage.ResourceView view = null; - - if (Type == FollowType.OutputColour) - { - view = core.CurD3D11PipelineState.m_OM.RenderTargets[index]; - } - else if (Type == FollowType.OutputDepth) - { - view = core.CurD3D11PipelineState.m_OM.DepthTarget; - } - else if (Type == FollowType.ReadWriteRes) - { - if (index >= core.CurD3D11PipelineState.m_OM.UAVStartSlot) - view = core.CurD3D11PipelineState.m_OM.UAVs[index]; - } - else if (Type == FollowType.InputResource) - { - view = core.CurD3D11PipelineState.m_PS.SRVs[index]; - } - - return view != null ? (int)view.FirstArraySlice : -1; - } - else - { - if (Type == FollowType.OutputColour) - { - return (int)core.CurGLPipelineState.m_FB.m_DrawFBO.Color[index].Layer; - } - else if (Type == FollowType.OutputDepth) - { - return (int)core.CurGLPipelineState.m_FB.m_DrawFBO.Depth.Layer; - } - else if (Type == FollowType.ReadWriteRes) - { - if(!core.CurGLPipelineState.Images[index].Layered) - return (int)core.CurGLPipelineState.Images[index].Layer; - } - else if (Type == FollowType.InputResource) - { - return (int)core.CurGLPipelineState.Textures[index].FirstSlice; - } - } - - return -1; + return GetBoundResource(core, arrayEl).FirstSlice; } public ResourceId GetResourceId(Core core) { - ResourceId id = ResourceId.Null; + return GetBoundResource(core, arrayEl).Id; + } + + public int GetBoundResourceArraySize(Core core) + { + if (Type == FollowType.ReadWrite) + { + var rw = GetReadWriteResources(core); + + var mapping = GetMapping(core); + + if (index < mapping.ReadWriteResources.Length) + { + var key = mapping.ReadWriteResources[index]; + + if (rw.ContainsKey(key)) + return rw[key].Length; + } + } + else if (Type == FollowType.ReadOnly) + { + var res = GetReadOnlyResources(core); + + var mapping = GetMapping(core); + + if (index < mapping.ReadOnlyResources.Length) + { + var key = mapping.ReadOnlyResources[index]; + + if (res.ContainsKey(key)) + return res[key].Length; + } + } + + return 1; + } + + public BoundResource GetBoundResource(Core core, int arrayIdx) + { + BoundResource ret = new BoundResource(); if (Type == FollowType.OutputColour) { var outputs = GetOutputTargets(core); - if(index < outputs.Length) - id = outputs[index]; + if (index < outputs.Length) + ret = outputs[index]; } else if (Type == FollowType.OutputDepth) { - id = GetDepthTarget(core); + ret = GetDepthTarget(core); } - else if (Type == FollowType.ReadWriteRes) + else if (Type == FollowType.ReadWrite) { var rw = GetReadWriteResources(core); - if (index < rw.Length) - id = rw[index]; + var mapping = GetMapping(core); + + if (index < mapping.ReadWriteResources.Length) + { + var key = mapping.ReadWriteResources[index]; + + if (rw.ContainsKey(key)) + ret = rw[key][arrayIdx]; + } } - else if (Type == FollowType.InputResource) + else if (Type == FollowType.ReadOnly) { - var res = GetResources(core); + var res = GetReadOnlyResources(core); - if(index < res.Length) - id = res[index]; + var mapping = GetMapping(core); + + if (index < mapping.ReadOnlyResources.Length) + { + var key = mapping.ReadOnlyResources[index]; + + if (res.ContainsKey(key)) + ret = res[key][arrayIdx]; + } } - return id; + return ret; } - public static ResourceId[] GetReadWriteResources(Core core) + public static BoundResource[] GetOutputTargets(Core core) { var curDraw = core.CurDrawcall; bool copy, compute; GetDrawContext(core, out copy, out compute); if (copy) - return new ResourceId[0]; - else if (compute) - return core.CurPipelineState.GetReadWriteResources(ShaderStageType.Compute); - else - return core.CurPipelineState.GetReadWriteResources(ShaderStageType.Pixel); - } - - public static ResourceId[] GetOutputTargets(Core core) - { - var curDraw = core.CurDrawcall; - bool copy, compute; - GetDrawContext(core, out copy, out compute); - - if (copy) - return new ResourceId[] { curDraw.copyDestination }; + return new BoundResource[] { new BoundResource(curDraw.copyDestination) }; else if(compute) - return new ResourceId[0]; + return new BoundResource[0]; else { var ret = core.CurPipelineState.GetOutputTargets(); @@ -280,44 +227,99 @@ namespace renderdocui.Windows if (ret.Length == 0 && curDraw != null && (curDraw.flags & DrawcallFlags.Present) != 0) { if (curDraw.copyDestination != ResourceId.Null) - return new ResourceId[] { curDraw.copyDestination }; + return new BoundResource[] { new BoundResource(curDraw.copyDestination) }; foreach (var t in core.CurTextures) if ((t.creationFlags & TextureCreationFlags.SwapBuffer) != 0) - return new ResourceId[] { t.ID }; + return new BoundResource[] { new BoundResource(t.ID) }; } return ret; } } - public static ResourceId GetDepthTarget(Core core) + public static BoundResource GetDepthTarget(Core core) { var curDraw = core.CurDrawcall; bool copy, compute; GetDrawContext(core, out copy, out compute); if (copy || compute) - return ResourceId.Null; + return new BoundResource(ResourceId.Null); else return core.CurPipelineState.GetDepthTarget(); } - public static ResourceId[] GetResources(Core core) + public Dictionary GetReadWriteResources(Core core) + { + return GetReadWriteResources(core, Stage); + } + + public static Dictionary GetReadWriteResources(Core core, ShaderStageType stage) { var curDraw = core.CurDrawcall; bool copy, compute; GetDrawContext(core, out copy, out compute); if (copy) - return new ResourceId[] { curDraw.copySource }; + { + return new Dictionary(); + } else if (compute) - return core.CurPipelineState.GetResources(ShaderStageType.Compute); + { + // only return compute resources for one stage + if (stage == ShaderStageType.Pixel) + return core.CurPipelineState.GetReadWriteResources(ShaderStageType.Compute); + else + return new Dictionary(); + } else - return core.CurPipelineState.GetResources(ShaderStageType.Pixel); + { + return core.CurPipelineState.GetReadWriteResources(stage); + } } - public static ShaderReflection GetReflection(Core core) + public Dictionary GetReadOnlyResources(Core core) + { + return GetReadOnlyResources(core, Stage); + } + + public static Dictionary GetReadOnlyResources(Core core, ShaderStageType stage) + { + var curDraw = core.CurDrawcall; + bool copy, compute; + GetDrawContext(core, out copy, out compute); + + if (copy) + { + var ret = new Dictionary(); + + // only return copy source for one stage + if(stage == ShaderStageType.Pixel) + ret.Add(new BindpointMap(0, 0), new BoundResource[] { new BoundResource(curDraw.copySource) }); + + return ret; + } + else if (compute) + { + // only return compute resources for one stage + if (stage == ShaderStageType.Pixel) + return core.CurPipelineState.GetReadOnlyResources(ShaderStageType.Compute); + else + return new Dictionary(); + } + else + { + return core.CurPipelineState.GetReadOnlyResources(stage); + } + } + + public ShaderReflection GetReflection(Core core) + { + return GetReflection(core, Stage); + } + + public static ShaderReflection GetReflection(Core core, ShaderStageType stage) { var curDraw = core.CurDrawcall; bool copy, compute; @@ -328,24 +330,46 @@ namespace renderdocui.Windows else if (compute) return core.CurPipelineState.GetShaderReflection(ShaderStageType.Compute); else - return core.CurPipelineState.GetShaderReflection(ShaderStageType.Pixel); + return core.CurPipelineState.GetShaderReflection(stage); } - public static ShaderBindpointMapping GetMapping(Core core) + public ShaderBindpointMapping GetMapping(Core core) + { + return GetMapping(core, Stage); + } + + public static ShaderBindpointMapping GetMapping(Core core, ShaderStageType stage) { var curDraw = core.CurDrawcall; bool copy, compute; GetDrawContext(core, out copy, out compute); if (copy) - return null; + { + ShaderBindpointMapping mapping = new ShaderBindpointMapping(); + mapping.ConstantBlocks = new BindpointMap[0]; + mapping.ReadWriteResources = new BindpointMap[0]; + mapping.InputAttributes = new int[0]; + + // for PS only add a single mapping to get the copy source + if (stage == ShaderStageType.Pixel) + mapping.ReadOnlyResources = new BindpointMap[] { new BindpointMap(0, 0) }; + else + mapping.ReadOnlyResources = new BindpointMap[0]; + + return mapping; + } else if (compute) + { return core.CurPipelineState.GetBindpointMapping(ShaderStageType.Compute); + } else - return core.CurPipelineState.GetBindpointMapping(ShaderStageType.Pixel); + { + return core.CurPipelineState.GetBindpointMapping(stage); + } } } - private Following m_Following = new Following(FollowType.OutputColour, 0); + private Following m_Following = Following.Default; public class TexSettings { @@ -424,7 +448,7 @@ namespace renderdocui.Windows FitToWindow = true; overlay.SelectedIndex = 0; - m_Following = new Following(FollowType.OutputColour, 0); + m_Following = Following.Default; texturefilter.SelectedIndex = 0; @@ -446,7 +470,7 @@ namespace renderdocui.Windows dockPanel.ActiveDocumentChanged += new EventHandler(dockPanel_ActiveDocumentChanged); - var w3 = Helpers.WrapDockContent(dockPanel, texPanel, "Inputs"); + var w3 = Helpers.WrapDockContent(dockPanel, roPanel, "Inputs"); w3.DockAreas &= ~DockAreas.Document; w3.DockState = DockState.DockRight; w3.Show(); @@ -454,7 +478,7 @@ namespace renderdocui.Windows w3.CloseButton = false; w3.CloseButtonVisible = false; - var w5 = Helpers.WrapDockContent(dockPanel, rtPanel, "Outputs"); + var w5 = Helpers.WrapDockContent(dockPanel, rwPanel, "Outputs"); w5.DockAreas &= ~DockAreas.Document; w5.DockState = DockState.DockRight; w5.Show(w3.Pane, w3); @@ -550,8 +574,8 @@ namespace renderdocui.Windows { Control[] persistors = { renderToolstripContainer, - texPanel, - rtPanel, + roPanel, + rwPanel, texlistContainer, pixelContextPanel }; @@ -580,8 +604,8 @@ namespace renderdocui.Windows { Control[] persistors = { renderToolstripContainer, - texPanel, - rtPanel, + roPanel, + rwPanel, texlistContainer, pixelContextPanel }; @@ -987,10 +1011,10 @@ namespace renderdocui.Windows saveTex.Enabled = true; - m_Following = new Following(FollowType.OutputColour, 0); + m_Following = Following.Default; - rtPanel.ClearThumbnails(); - texPanel.ClearThumbnails(); + rwPanel.ClearThumbnails(); + roPanel.ClearThumbnails(); m_HighWaterStatusLength = 0; @@ -1049,8 +1073,8 @@ namespace renderdocui.Windows saveTex.Enabled = false; - rtPanel.ClearThumbnails(); - texPanel.ClearThumbnails(); + rwPanel.ClearThumbnails(); + roPanel.ClearThumbnails(); texturefilter.SelectedIndex = 0; @@ -1099,369 +1123,270 @@ namespace renderdocui.Windows UI_UpdateChannels(); } + private void InitResourcePreview(ResourcePreview prev, ResourceId id, bool force, Following follow, string bindName, string slotName) + { + if (id != ResourceId.Null || force) + { + FetchTexture tex = null; + foreach (var t in m_Core.CurTextures) + if (t.ID == id) + tex = t; + + FetchBuffer buf = null; + foreach (var b in m_Core.CurBuffers) + if (b.ID == id) + buf = b; + + if (tex != null) + { + string fullname = bindName; + if (tex.customName) + { + if (fullname.Length > 0) + fullname += " = "; + fullname += tex.name; + } + if (fullname.Length == 0) + fullname = tex.name; + + prev.Init(fullname, tex.width, tex.height, tex.depth, tex.mips); + IntPtr handle = prev.ThumbnailHandle; + m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => + { + m_Output.AddThumbnail(handle, id); + }); + } + else if (buf != null) + { + string fullname = bindName; + if (buf.customName) + { + if (fullname.Length > 0) + fullname += " = "; + fullname += buf.name; + } + if (fullname.Length == 0) + fullname = buf.name; + + prev.Init(fullname, buf.length, 0, 0, Math.Max(1, buf.structureSize)); + IntPtr handle = prev.ThumbnailHandle; + m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => + { + m_Output.AddThumbnail(handle, ResourceId.Null); + }); + } + else + { + prev.Init(); + } + + prev.Tag = follow; + prev.SlotName = slotName; + prev.Visible = true; + } + else if (prev.Selected) + { + FetchTexture tex = null; + + if(id != ResourceId.Null) + foreach (var t in m_Core.CurTextures) + if (t.ID == id) + tex = t; + + IntPtr handle = prev.ThumbnailHandle; + if (id == ResourceId.Null || tex == null) + prev.Init(); + else + prev.Init("Unused", tex.width, tex.height, tex.depth, tex.mips); + m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => + { + m_Output.AddThumbnail(handle, ResourceId.Null); + }); + } + else + { + prev.Init(); + prev.Visible = false; + } + } + + private void InitStageResourcePreviews(ShaderStageType stage, ShaderResource[] resourceDetails, BindpointMap[] mapping, + Dictionary ResList, + ThumbnailStrip prevs, ref int prevIndex, + bool copy, bool rw) + { + for (int idx = 0; idx < mapping.Length; idx++) + { + var key = mapping[idx]; + + BoundResource[] resArray = null; + + if (ResList.ContainsKey(key)) + resArray = ResList[key]; + + int arrayLen = resArray != null ? resArray.Length : 1; + + for (int arrayIdx = 0; arrayIdx < arrayLen; arrayIdx++) + { + ResourceId id = resArray != null ? resArray[arrayIdx].Id : ResourceId.Null; + + bool used = key.used; + bool skip = false; + + string bindName = ""; + + foreach (var bind in resourceDetails) + { + if (bind.bindPoint == idx && bind.IsSRV) + { + bindName = bind.name; + break; + } + } + + if (skip) + continue; + + if (copy) + { + used = true; + bindName = "Source"; + } + + Following follow = new Following(rw ? FollowType.ReadWrite : FollowType.ReadOnly, stage, idx, arrayIdx); + string slotName = String.Format("{0} {1}{2}", m_Core.CurPipelineState.Abbrev(stage), rw ? "RW " : "", idx); + + if (arrayLen > 1) + slotName += String.Format("[{0}]", arrayIdx); + + if (copy) + slotName = "SRC"; + + // show if + bool show = (used || // it's referenced by the shader - regardless of empty or not + (showDisabled.Checked && !used && id != ResourceId.Null) || // it's bound, but not referenced, and we have "show disabled" + (showEmpty.Checked && id == ResourceId.Null) // it's empty, and we have "show empty" + ); + + ResourcePreview prev; + + if (prevIndex < prevs.Thumbnails.Length) + { + prev = prevs.Thumbnails[prevIndex]; + } + else + { + // don't create it if we're not actually going to show it + if (!show) + continue; + + prev = UI_CreateThumbnail(prevs); + } + + prevIndex++; + + InitResourcePreview(prev, show ? id : ResourceId.Null, show, follow, bindName, slotName); + } + } + } + public void OnEventSelected(UInt32 frameID, UInt32 eventID) { if (IsDisposed) return; UI_OnTextureSelectionChanged(); - ResourceId[] RTs = Following.GetOutputTargets(m_Core); - ResourceId[] RWs = Following.GetReadWriteResources(m_Core); - ResourceId Depth = Following.GetDepthTarget(m_Core); - ResourceId[] Texs = Following.GetResources(m_Core); + UI_CreateThumbnails(); - ShaderReflection details = Following.GetReflection(m_Core); - ShaderBindpointMapping mapping = Following.GetMapping(m_Core); + if (m_Output == null) return; + + BoundResource[] RTs = Following.GetOutputTargets(m_Core); + BoundResource Depth = Following.GetDepthTarget(m_Core); + + int rwIndex = 0; + int roIndex = 0; var curDraw = m_Core.GetDrawcall(frameID, eventID); bool copy = curDraw != null && (curDraw.flags & (DrawcallFlags.Copy|DrawcallFlags.Resolve)) != 0; - if (m_Output == null) return; - - UI_CreateThumbnails(); - - int i = 0; for(int rt=0; rt < RTs.Length; rt++) { ResourcePreview prev; - if (i < rtPanel.Thumbnails.Length) - prev = rtPanel.Thumbnails[i]; + if (rwIndex < rwPanel.Thumbnails.Length) + prev = rwPanel.Thumbnails[rwIndex]; else - prev = UI_CreateThumbnail(rtPanel); + prev = UI_CreateThumbnail(rwPanel); - if (RTs[rt] != ResourceId.Null) - { - FetchTexture tex = null; - foreach (var t in m_Core.CurTextures) - if (t.ID == RTs[rt]) - tex = t; + rwIndex++; - FetchBuffer buf = null; - foreach (var b in m_Core.CurBuffers) - if (b.ID == RTs[rt]) - buf = b; + Following follow = new Following(FollowType.OutputColour, ShaderStageType.Pixel, rt, 0); + string bindName = copy ? "Destination" : ""; + string slotName = copy ? "DST" : String.Format("{0}{1}", m_Core.CurPipelineState.OutputAbbrev(), rt); - string bindName = ""; - - if (copy) - bindName = "Destination"; - - if (tex != null) - { - prev.Init(!tex.customName && bindName.Length > 0 ? bindName : tex.name, tex.width, tex.height, tex.depth, tex.mips); - IntPtr handle = prev.ThumbnailHandle; - ResourceId id = RTs[rt]; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, id); - }); - } - else if (buf != null) - { - prev.Init(!buf.customName && bindName.Length > 0 ? bindName : buf.name, buf.length, 0, 0, Math.Max(1, buf.structureSize)); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } - else - { - prev.Init(); - } - - prev.Tag = new Following(FollowType.OutputColour, rt); - prev.SlotName = copy ? "DST" : rt.ToString(); - prev.Visible = true; - } - else if (prev.Selected) - { - prev.Init(); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } - else - { - prev.Init(); - prev.Visible = false; - } - - i++; + InitResourcePreview(prev, RTs[rt].Id, false, follow, bindName, slotName); } - for (int rw = 0; rw < RWs.Length; rw++) + // depth { ResourcePreview prev; - - if (i < rtPanel.Thumbnails.Length) - prev = rtPanel.Thumbnails[i]; - else - prev = UI_CreateThumbnail(rtPanel); - FetchTexture tex = null; - foreach (var t in m_Core.CurTextures) - if (t.ID == RWs[rw]) - tex = t; - - FetchBuffer buf = null; - foreach (var b in m_Core.CurBuffers) - if (b.ID == RWs[rw]) - buf = b; - - bool used = false; - - string bindName = ""; - - if (details != null) - { - foreach (var bind in details.ReadWriteResources) - { - if (mapping.ReadWriteResources[bind.bindPoint].bind == rw) - { - used = true; - bindName = "<" + bind.name + ">"; - } - } - } - - // show if - if (used || // it's referenced by the shader - regardless of empty or not - (showDisabled.Checked && !used && RWs[rw] != ResourceId.Null) || // it's bound, but not referenced, and we have "show disabled" - (showEmpty.Checked && RWs[rw] == ResourceId.Null) // it's empty, and we have "show empty" - ) - { - if (tex != null) - { - prev.Init(!tex.customName && bindName.Length > 0 ? bindName : tex.name, tex.width, tex.height, tex.depth, tex.mips); - IntPtr handle = prev.ThumbnailHandle; - ResourceId id = RWs[rw]; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, id); - }); - } - else if (buf != null) - { - prev.Init(!buf.customName && bindName.Length > 0 ? bindName : buf.name, buf.length, 0, 0, Math.Max(1, buf.structureSize)); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } - else - { - prev.Init(); - } - - prev.Tag = new Following(FollowType.ReadWriteRes, rw); - prev.SlotName = "RW" + rw; - prev.Visible = true; - } - else if (prev.Selected) - { - prev.Init(); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } + if (rwIndex < rwPanel.Thumbnails.Length) + prev = rwPanel.Thumbnails[rwIndex]; else - { - prev.Init(); - prev.Visible = false; - } + prev = UI_CreateThumbnail(rwPanel); - i++; + rwIndex++; + + Following follow = new Following(FollowType.OutputDepth, ShaderStageType.Pixel, 0, 0); + + InitResourcePreview(prev, Depth.Id, false, follow, "", "DS"); } + ShaderStageType[] stages = new ShaderStageType[] { + ShaderStageType.Vertex, + ShaderStageType.Hull, + ShaderStageType.Domain, + ShaderStageType.Geometry, + ShaderStageType.Pixel + }; + // display resources used for all stages + foreach (ShaderStageType stage in stages) { - ResourcePreview prev; + Dictionary RWs = Following.GetReadWriteResources(m_Core, stage); + Dictionary ROs = Following.GetReadOnlyResources(m_Core, stage); - // depth thumbnail is always the last one - if (i < rtPanel.Thumbnails.Length) - { - // hide others - for (; i < rtPanel.Thumbnails.Length - 1; i++) - { - rtPanel.Thumbnails[i].Init(); - rtPanel.Thumbnails[i].Visible = false; - } - prev = rtPanel.Thumbnails[rtPanel.Thumbnails.Length - 1]; - } - else - { - prev = UI_CreateThumbnail(rtPanel); - } + ShaderReflection details = Following.GetReflection(m_Core, stage); + ShaderBindpointMapping mapping = Following.GetMapping(m_Core, stage); - if (Depth != ResourceId.Null) - { - FetchTexture tex = null; - foreach (var t in m_Core.CurTextures) - if (t.ID == Depth) - tex = t; + InitStageResourcePreviews(stage, + details != null ? details.ReadWriteResources : new ShaderResource[0], + mapping.ReadWriteResources, + RWs, rwPanel, ref rwIndex, copy, true); - FetchBuffer buf = null; - foreach (var b in m_Core.CurBuffers) - if (b.ID == Depth) - buf = b; - - if (tex != null) - { - prev.Init(tex.name, tex.width, tex.height, tex.depth, tex.mips); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, Depth); - }); - } - else if (buf != null) - { - prev.Init(buf.name, buf.length, 0, 0, Math.Max(1, buf.structureSize)); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } - else - { - prev.Init(); - } - - prev.Tag = new Following(FollowType.OutputDepth, 0); - prev.SlotName = "D"; - prev.Visible = true; - } - else if (prev.Selected) - { - prev.Init(); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } - else - { - prev.Init(); - prev.Visible = false; - } + InitStageResourcePreviews(stage, + details != null ? details.ReadOnlyResources : new ShaderResource[0], + mapping.ReadOnlyResources, + ROs, roPanel, ref roIndex, copy, false); } - rtPanel.RefreshLayout(); + // hide others + for (; rwIndex < rwPanel.Thumbnails.Length - 1; rwIndex++) + { + rwPanel.Thumbnails[rwIndex].Init(); + rwPanel.Thumbnails[rwIndex].Visible = false; + } - // VKTODOMED all this needs to be updated to account for multiple descriptor sets - // VKTODOMED also needs to handle array descriptors (ie. not texture arrays) + rwPanel.RefreshLayout(); - i = 0; - for(; i < Texs.Length; i++) + for (; roIndex < roPanel.Thumbnails.Length - 1; roIndex++) { - ResourcePreview prev; - - if (i < texPanel.Thumbnails.Length) - prev = texPanel.Thumbnails[i]; - else - prev = UI_CreateThumbnail(texPanel); - - bool used = false; - - string bindName = ""; - - if (details != null) - { - foreach (var bind in details.ReadOnlyResources) - { - if (mapping.ReadOnlyResources[bind.bindPoint].bind == i && bind.IsSRV) - { - used = true; - bindName = "<" + bind.name + ">"; - break; - } - } - } - - if (copy) - { - used = true; - bindName = "Source"; - } - - // show if - if (used || // it's referenced by the shader - regardless of empty or not - (showDisabled.Checked && !used && Texs[i] != ResourceId.Null) || // it's bound, but not referenced, and we have "show disabled" - (showEmpty.Checked && Texs[i] == ResourceId.Null) // it's empty, and we have "show empty" - ) - { - FetchTexture tex = null; - foreach (var t in m_Core.CurTextures) - if (t.ID == Texs[i]) - tex = t; - - FetchBuffer buf = null; - foreach (var b in m_Core.CurBuffers) - if (b.ID == Texs[i]) - buf = b; - - if (tex != null) - { - prev.Init(!tex.customName && bindName.Length > 0 ? bindName : tex.name, tex.width, tex.height, tex.depth, tex.mips); - IntPtr handle = prev.ThumbnailHandle; - ResourceId id = Texs[i]; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, id); - }); - } - else if (buf != null) - { - prev.Init(!buf.customName && bindName.Length > 0 ? bindName : buf.name, buf.length, 0, 0, Math.Max(1, buf.structureSize)); - IntPtr handle = prev.ThumbnailHandle; - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } - else - { - prev.Init(); - } - - prev.Tag = new Following(FollowType.InputResource, i); - prev.SlotName = copy ? "SRC" : i.ToString(); - prev.Visible = true; - } - else if (prev.Selected) - { - FetchTexture tex = null; - foreach (var t in m_Core.CurTextures) - if (t.ID == Texs[i]) - tex = t; - - IntPtr handle = prev.ThumbnailHandle; - if (Texs[i] == ResourceId.Null || tex == null) - prev.Init(); - else - prev.Init("Unused", tex.width, tex.height, tex.depth, tex.mips); - m_Core.Renderer.BeginInvoke((ReplayRenderer rep) => - { - m_Output.AddThumbnail(handle, ResourceId.Null); - }); - } - else - { - prev.Init(); - prev.Visible = false; - } + roPanel.Thumbnails[roIndex].Init(); + roPanel.Thumbnails[roIndex].Visible = false; } - texPanel.RefreshLayout(); + roPanel.RefreshLayout(); m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay); @@ -1486,25 +1411,25 @@ namespace renderdocui.Windows private void UI_CreateThumbnails() { - if (rtPanel.Thumbnails.Length > 0 || texPanel.Thumbnails.Length > 0) return; + if (rwPanel.Thumbnails.Length > 0 || roPanel.Thumbnails.Length > 0) return; - rtPanel.SuspendLayout(); - texPanel.SuspendLayout(); + rwPanel.SuspendLayout(); + roPanel.SuspendLayout(); // these will expand, but we make sure that there is a good set reserved for (int i = 0; i < 9; i++) { - var prev = UI_CreateThumbnail(rtPanel); + var prev = UI_CreateThumbnail(rwPanel); if(i == 0) prev.Selected = true; } for (int i = 0; i < 128; i++) - UI_CreateThumbnail(texPanel); + UI_CreateThumbnail(roPanel); - rtPanel.ResumeLayout(); - texPanel.ResumeLayout(); + rwPanel.ResumeLayout(); + roPanel.ResumeLayout(); } private int prevFirstArraySlice = -1; @@ -1864,10 +1789,10 @@ namespace renderdocui.Windows case FollowType.OutputDepth: m_PreviewPanel.Text = string.Format("Cur Depth Output - {0}", name); break; - case FollowType.ReadWriteRes: + case FollowType.ReadWrite: m_PreviewPanel.Text = string.Format("Cur RW Output - {0}", name); break; - case FollowType.InputResource: + case FollowType.ReadOnly: m_PreviewPanel.Text = string.Format("Cur Input {0} - {1}", m_Following.index, name); break; } @@ -1882,10 +1807,10 @@ namespace renderdocui.Windows case FollowType.OutputDepth: m_PreviewPanel.Text = string.Format("Cur Depth Output"); break; - case FollowType.ReadWriteRes: + case FollowType.ReadWrite: m_PreviewPanel.Text = string.Format("Cur RW Output"); break; - case FollowType.InputResource: + case FollowType.ReadOnly: m_PreviewPanel.Text = string.Format("Cur Input {0}", m_Following.index); break; } @@ -2336,10 +2261,10 @@ namespace renderdocui.Windows return; } - foreach (var prev in rtPanel.Thumbnails) + foreach (var prev in rwPanel.Thumbnails) if (prev.Unbound) prev.Clear(); - foreach (var prev in texPanel.Thumbnails) + foreach (var prev in roPanel.Thumbnails) if (prev.Unbound) prev.Clear(); m_Core.Renderer.Invoke((ReplayRenderer r) => { if (m_Output != null) m_Output.Display(); }); @@ -3668,10 +3593,10 @@ namespace renderdocui.Windows var follow = (Following)prev.Tag; - foreach (var p in rtPanel.Thumbnails) + foreach (var p in rwPanel.Thumbnails) p.Selected = false; - foreach (var p in texPanel.Thumbnails) + foreach (var p in roPanel.Thumbnails) p.Selected = false; m_Following = follow;