public VulkanPipelineState GetVulkanPipelineState() { IntPtr mem = CustomMarshal.Alloc(typeof(VulkanPipelineState)); bool success = ReplayRenderer_GetVulkanPipelineState(m_Real, mem); VulkanPipelineState ret = null; if (success) { ret = (VulkanPipelineState)CustomMarshal.PtrToStructure(mem, typeof(VulkanPipelineState), true); } CustomMarshal.Free(mem); return(ret); }
private void ShowShaderViewer(VulkanPipelineState.ShaderStage stage, Dictionary<String, String> files) { VulkanPipelineStateViewer pipeviewer = this; ShaderViewer sv = new ShaderViewer(m_Core, false, "main", files, // Save Callback (ShaderViewer viewer, Dictionary<string, string> updatedfiles) => { string compileSource = ""; foreach (var kv in updatedfiles) compileSource += kv.Value; // invoke off to the ReplayRenderer to replace the log's shader // with our edited one m_Core.Renderer.BeginInvoke((ReplayRenderer r) => { string errs = ""; ResourceId from = stage.Shader; ResourceId to = r.BuildTargetShader("main", compileSource, stage.ShaderDetails.DebugInfo.compileFlags, stage.stage, out errs); viewer.BeginInvoke((MethodInvoker)delegate { viewer.ShowErrors(errs); }); if (to == ResourceId.Null) { r.RemoveReplacement(from); pipeviewer.BeginInvoke((MethodInvoker)delegate { m_Core.RefreshStatus(); }); } else { r.ReplaceResource(from, to); pipeviewer.BeginInvoke((MethodInvoker)delegate { m_Core.RefreshStatus(); }); } }); }, // Close Callback () => { // remove the replacement on close (we could make this more sophisticated if there // was a place to control replaced resources/shaders). m_Core.Renderer.BeginInvoke((ReplayRenderer r) => { r.RemoveReplacement(stage.Shader); pipeviewer.BeginInvoke((MethodInvoker)delegate { m_Core.RefreshStatus(); }); }); }); sv.Show(m_DockContent.DockPanel); }
private void ShowCBuffer(VulkanPipelineState.ShaderStage stage, CBufferTag tag) { if (tag == null || tag.slotIdx == uint.MaxValue) return; VulkanPipelineState.Pipeline pipe = m_Core.CurVulkanPipelineState.graphics; if(stage.stage == ShaderStageType.Compute) pipe = m_Core.CurVulkanPipelineState.compute; var existing = ConstantBufferPreviewer.Has(stage.stage, tag.slotIdx, tag.arrayIdx); if (existing != null) { existing.Show(); return; } var prev = new ConstantBufferPreviewer(m_Core, stage.stage, tag.slotIdx, tag.arrayIdx); prev.ShowDock(m_DockContent.Pane, DockAlignment.Right, 0.3); }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, VulkanPipelineState.ShaderStage stage, VulkanPipelineState.Pipeline pipe, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView cbuffers) { ShaderReflection shaderDetails = stage.ShaderDetails; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0) { if (shaderDetails.DebugInfo.files.Length > 0 || shaderDetails.DebugInfo.entryFunc != "main") shader.Text = shaderDetails.DebugInfo.entryFunc + "()"; if (shaderDetails.DebugInfo.files.Length > 0) { string shaderfn = ""; int entryFile = shaderDetails.DebugInfo.entryFile; if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length) entryFile = 0; shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename; shader.Text += " - " + shaderfn; } } int vs = 0; vs = resources.VScrollValue(); resources.BeginUpdate(); resources.Nodes.Clear(); var samplers = new Dictionary<ResourceId, SamplerData>(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { AddResourceRow(shaderDetails, stage, bindset, bind, pipe, resources, texs, bufs, ref samplers); } // if we have a shader bound, go through and add rows for any resources it wants for binds that aren't // in this descriptor set (e.g. if layout mismatches) if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind >= pipe.DescSets[bindset].bindings.Length) { AddResourceRow(shaderDetails, stage, bindset, stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind >= pipe.DescSets[bindset].bindings.Length) { AddResourceRow(shaderDetails, stage, bindset, stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } } } // if we have a shader bound, go through and add rows for any resources it wants for descriptor sets that aren't // bound at all if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset >= pipe.DescSets.Length) { AddResourceRow(shaderDetails, stage, stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset, stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset >= pipe.DescSets.Length) { AddResourceRow(shaderDetails, stage, stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset, stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind, pipe, resources, texs, bufs, ref samplers); } } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { AddConstantBlockRow(shaderDetails, stage, bindset, bind, pipe, cbuffers, bufs); } // if we have a shader bound, go through and add rows for any cblocks it wants for binds that aren't // in this descriptor set (e.g. if layout mismatches) if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ConstantBlocks.Length; i++) { var cb = shaderDetails.ConstantBlocks[i]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset && stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind >= pipe.DescSets[bindset].bindings.Length) { AddConstantBlockRow(shaderDetails, stage, bindset, stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind, pipe, cbuffers, bufs); } } } } // if we have a shader bound, go through and add rows for any resources it wants for descriptor sets that aren't // bound at all if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ConstantBlocks.Length; i++) { var cb = shaderDetails.ConstantBlocks[i]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset >= pipe.DescSets.Length && cb.bufferBacked) { AddConstantBlockRow(shaderDetails, stage, stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset, stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind, pipe, cbuffers, bufs); } } } // search for push constants and add them last if (shaderDetails != null) { for (int cb = 0; cb < shaderDetails.ConstantBlocks.Length; cb++) { var cblock = shaderDetails.ConstantBlocks[cb]; if (cblock.bufferBacked == false) { // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. var node = cbuffers.Nodes.Add(new object[] { "", "", cblock.name, "Push constants", "", String.Format("{0} Variables", cblock.variables.Length) }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag((uint)cb, 0); } } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); }
public void SetStates(APIProperties props, D3D11PipelineState d3d11, GLPipelineState gl, VulkanPipelineState vk) { m_APIProps = props; m_D3D11 = d3d11; m_GL = gl; m_Vulkan = vk; }
// setting a context filter allows replaying of deferred events. You can set the deferred // events to replay in a context, after replaying up to a given event on the main thread public void SetContextFilter(ILogViewerForm exclude, UInt32 frameID, UInt32 eventID, ResourceId ctx, UInt32 firstDeferred, UInt32 lastDeferred) { m_FrameID = frameID; m_EventID = eventID; m_DeferredEvent = lastDeferred; m_Renderer.Invoke((ReplayRenderer r) => { r.SetContextFilter(ctx, firstDeferred, lastDeferred); }); m_Renderer.Invoke((ReplayRenderer r) => { r.SetFrameEvent(m_FrameID, m_EventID, true); m_D3D11PipelineState = r.GetD3D11PipelineState(); m_GLPipelineState = r.GetGLPipelineState(); m_VulkanPipelineState = r.GetVulkanPipelineState(); m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState, m_VulkanPipelineState); }); foreach (var logviewer in m_LogViewers) { if (logviewer == exclude) continue; Control c = (Control)logviewer; if (c.InvokeRequired) c.BeginInvoke(new Action(() => logviewer.OnEventSelected(frameID, eventID))); else logviewer.OnEventSelected(frameID, eventID); } }
public void CloseLogfile() { if (!m_LogLoaded) return; m_LogFile = ""; m_Renderer.CloseThreadSync(); m_Renderer = new RenderManager(); m_APIProperties = null; m_FrameInfo = null; m_DrawCalls = null; m_Buffers = null; m_Textures = null; m_D3D11PipelineState = null; m_GLPipelineState = null; m_VulkanPipelineState = null; m_PipelineState.SetStates(null, null, null, null); DebugMessages.Clear(); UnreadMessageCount = 0; m_LogLoaded = false; if (m_LogWatcher != null) m_LogWatcher.EnableRaisingEvents = false; m_LogWatcher = null; foreach (var logviewer in m_LogViewers) { Control c = (Control)logviewer; if (c.InvokeRequired) c.Invoke(new Action(() => logviewer.OnLogfileClosed())); else logviewer.OnLogfileClosed(); } }
private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.VertexInput vi) { { writer.WriteStartElement("h3"); writer.WriteString("Attributes"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); foreach (var attr in vi.attrs) rows.Add(new object[] { attr.location, attr.binding, attr.format.ToString(), attr.byteoffset }); ExportHTMLTable(writer, new string[] { "Location", "Binding", "Format", "Offset" }, rows.ToArray()); } { writer.WriteStartElement("h3"); writer.WriteString("Bindings"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); foreach (var attr in vi.binds) rows.Add(new object[] { attr.vbufferBinding, attr.bytestride, attr.perInstance ? "PER_INSTANCE" : "PER_VERTEX" }); ExportHTMLTable(writer, new string[] { "Binding", "Byte Stride", "Step Rate" }, rows.ToArray()); } { writer.WriteStartElement("h3"); writer.WriteString("Vertex Buffers"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); int i = 0; foreach (var vb in vi.vbuffers) { string name = "Buffer " + vb.buffer.ToString(); ulong length = 0; if (vb.buffer == ResourceId.Null) { continue; } else { FetchBuffer buf = m_Core.GetBuffer(vb.buffer); if(buf != null) { name = buf.name; length = buf.length; } } rows.Add(new object[] { i, name, vb.offset, length }); i++; } ExportHTMLTable(writer, new string[] { "Binding", "Buffer", "Offset", "Byte Length" }, rows.ToArray()); } }
private bool HasImportantViewParams(VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement view, FetchTexture tex) { // Since mutable formats are more unclear in vulkan (it allows casting between any // similar format, and the underlying texture still has a valid format), we consider // a format difference to be important even though we display the view's format in // the row data itself if (view.viewfmt != tex.format || view.baseMip > 0 || view.baseLayer > 0 || (view.numMip < tex.mips && tex.mips > 1) || (view.numLayer < tex.arraysize && tex.arraysize > 1)) return true; return false; }
private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.CurrentPass pass) { { writer.WriteStartElement("h3"); writer.WriteString("Framebuffer"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Width", "Height", "Layers" }, new object[] { pass.framebuffer.width, pass.framebuffer.height, pass.framebuffer.layers }); List<object[]> rows = new List<object[]>(); int i = 0; foreach (var a in pass.framebuffer.attachments) { FetchTexture tex = m_Core.GetTexture(a.img); string name = "Image " + a.img; if (tex != null) name = tex.name; rows.Add(new object[] { i, name, a.baseMip, a.numMip, a.baseLayer, a.numLayer }); i++; } ExportHTMLTable(writer, new string[] { "Slot", "Image", "First mip", "Number of mips", "First array layer", "Number of layers", }, rows.ToArray()); } { writer.WriteStartElement("h3"); writer.WriteString("Render Pass"); writer.WriteEndElement(); if (pass.renderpass.inputAttachments.Length > 0) { object[][] inputs = new object[pass.renderpass.inputAttachments.Length][]; for (int i = 0; i < pass.renderpass.inputAttachments.Length; i++) inputs[i] = new object[] { pass.renderpass.inputAttachments[i] }; ExportHTMLTable(writer, new string[] { "Input Attachment", }, inputs); writer.WriteStartElement("p"); writer.WriteEndElement(); } if (pass.renderpass.colorAttachments.Length > 0) { object[][] colors = new object[pass.renderpass.colorAttachments.Length][]; for (int i = 0; i < pass.renderpass.colorAttachments.Length; i++) colors[i] = new object[] { pass.renderpass.colorAttachments[i] }; ExportHTMLTable(writer, new string[] { "Color Attachment", }, colors); writer.WriteStartElement("p"); writer.WriteEndElement(); } if (pass.renderpass.depthstencilAttachment >= 0) { writer.WriteStartElement("p"); writer.WriteString("Depth-stencil Attachment: " + pass.renderpass.depthstencilAttachment); writer.WriteEndElement(); } } { writer.WriteStartElement("h3"); writer.WriteString("Render Area"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "X", "Y", "Width", "Height" }, new object[] { pass.renderArea.x, pass.renderArea.y, pass.renderArea.width, pass.renderArea.height }); } }
private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.DepthStencil ds) { { writer.WriteStartElement("h3"); writer.WriteString("Depth State"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Depth Test Enable", "Depth Writes Enable", "Depth Function", "Depth Bounds" }, new object[] { ds.depthTestEnable ? "Yes" : "No", ds.depthWriteEnable ? "Yes" : "No", ds.depthCompareOp, ds.depthBoundsEnable ? String.Format("{0} - {1}", Formatter.Format(ds.minDepthBounds), Formatter.Format(ds.maxDepthBounds)) : "Disabled", }); } { writer.WriteStartElement("h3"); writer.WriteString("Stencil State"); writer.WriteEndElement(); if (ds.stencilTestEnable) { List<object[]> rows = new List<object[]>(); rows.Add(new object[] { "Front", ds.front.stencilref.ToString("X2"), ds.front.compareMask.ToString("X2"), ds.front.writeMask.ToString("X2"), ds.front.func, ds.front.passOp, ds.front.failOp, ds.front.depthFailOp }); rows.Add(new object[] { "Back", ds.back.stencilref.ToString("X2"), ds.back.compareMask.ToString("X2"), ds.back.writeMask.ToString("X2"), ds.back.func, ds.back.passOp, ds.back.failOp, ds.back.depthFailOp }); ExportHTMLTable(writer, new string[] { "Face", "Ref", "Compare Mask", "Write Mask", "Function", "Pass Op", "Fail Op", "Depth Fail Op" }, rows.ToArray()); } else { writer.WriteStartElement("p"); writer.WriteString("Disabled"); writer.WriteEndElement(); } } }
private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.ColorBlend cb) { writer.WriteStartElement("h3"); writer.WriteString("Color Blend State"); writer.WriteEndElement(); var blendConst = Formatter.Format(cb.blendConst[0]) + ", " + Formatter.Format(cb.blendConst[1]) + ", " + Formatter.Format(cb.blendConst[2]) + ", " + Formatter.Format(cb.blendConst[3]); ExportHTMLTable(writer, new string[] { "Alpha to Coverage", "Alpha to One", "Logic Op", "Blend Constant" }, new object[] { cb.alphaToCoverageEnable ? "Yes" : "No", cb.alphaToOneEnable ? "Yes" : "No", cb.logicOpEnable ? cb.LogicOp : "Disabled", blendConst, }); writer.WriteStartElement("h3"); writer.WriteString("Attachment Blends"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); int i = 0; foreach (var b in cb.attachments) { rows.Add(new object[] { i, b.blendEnable ? "Yes" : "No", b.m_Blend.Source, b.m_Blend.Destination, b.m_Blend.Operation, b.m_AlphaBlend.Source, b.m_AlphaBlend.Destination, b.m_AlphaBlend.Operation, ((b.WriteMask & 0x1) == 0 ? "_" : "R") + ((b.WriteMask & 0x2) == 0 ? "_" : "G") + ((b.WriteMask & 0x4) == 0 ? "_" : "B") + ((b.WriteMask & 0x8) == 0 ? "_" : "A") }); i++; } ExportHTMLTable(writer, new string[] { "Slot", "Blend Enable", "Blend Source", "Blend Destination", "Blend Operation", "Alpha Blend Source", "Alpha Blend Destination", "Alpha Blend Operation", "Write Mask", }, rows.ToArray()); }
private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.Raster rs) { { writer.WriteStartElement("h3"); writer.WriteString("Raster State"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Fill Mode", "Cull Mode", "Front CCW" }, new object[] { rs.FillMode, rs.CullMode, rs.FrontCCW ? "Yes" : "No" }); writer.WriteStartElement("p"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Depth Clip Enable", "Rasterizer Discard Enable" }, new object[] { rs.depthClampEnable ? "Yes" : "No", rs.rasterizerDiscardEnable ? "Yes" : "No" }); writer.WriteStartElement("p"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Depth Bias", "Depth Bias Clamp", "Slope Scaled Bias", "Line Width" }, new object[] { Formatter.Format(rs.depthBias), Formatter.Format(rs.depthBiasClamp), Formatter.Format(rs.slopeScaledDepthBias), Formatter.Format(rs.lineWidth) }); } VulkanPipelineState.MultiSample msaa = m_Core.CurVulkanPipelineState.MSAA; { writer.WriteStartElement("h3"); writer.WriteString("Multisampling State"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Raster Samples", "Sample-rate shading", "Min Sample Shading Rate", "Sample Mask" }, new object[] { msaa.rasterSamples, msaa.sampleShadingEnable ? "Yes" : "No", Formatter.Format(msaa.minSampleShading), msaa.sampleMask.ToString("X8") }); } VulkanPipelineState.ViewState vp = m_Core.CurVulkanPipelineState.VP; { writer.WriteStartElement("h3"); writer.WriteString("Viewports"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); int i = 0; foreach (var vs in vp.viewportScissors) { var v = vs.vp; rows.Add(new object[] { i, v.x, v.y, v.Width, v.Height, v.MinDepth, v.MaxDepth }); i++; } ExportHTMLTable(writer, new string[] { "Slot", "X", "Y", "Width", "Height", "Min Depth", "Max Depth" }, rows.ToArray()); } { writer.WriteStartElement("h3"); writer.WriteString("Scissors"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); int i = 0; foreach (var vs in vp.viewportScissors) { var s = vs.scissor; rows.Add(new object[] { i, s.x, s.y, s.width, s.height }); i++; } ExportHTMLTable(writer, new string[] { "Slot", "X", "Y", "Width", "Height" }, rows.ToArray()); } }
private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.ShaderStage sh) { ShaderReflection shaderDetails = sh.ShaderDetails; { writer.WriteStartElement("h3"); writer.WriteString("Shader"); writer.WriteEndElement(); string shadername = "Unknown"; if (sh.Shader == ResourceId.Null) shadername = "Unbound"; else shadername = sh.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0 && shaderDetails.DebugInfo.files.Length > 0) shadername = shaderDetails.DebugInfo.entryFunc + "()" + " - " + shaderDetails.DebugInfo.files[0].BaseFilename; writer.WriteStartElement("p"); writer.WriteString(shadername); writer.WriteEndElement(); if (sh.Shader == ResourceId.Null) return; } var pipeline = (sh.stage == ShaderStageType.Compute ? m_Core.CurVulkanPipelineState.compute : m_Core.CurVulkanPipelineState.graphics); if(shaderDetails.ConstantBlocks.Length > 0) { writer.WriteStartElement("h3"); writer.WriteString("UBOs"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); for(int i=0; i < shaderDetails.ConstantBlocks.Length; i++) { var b = shaderDetails.ConstantBlocks[i]; var bindMap = sh.BindpointMapping.ConstantBlocks[i]; if (!bindMap.used) continue; var set = pipeline.DescSets[sh.BindpointMapping.ConstantBlocks[i].bindset]; var bind = set.bindings[sh.BindpointMapping.ConstantBlocks[i].bind]; string setname = bindMap.bindset.ToString(); string slotname = bindMap.bind.ToString(); slotname += ": " + b.name; for (int a = 0; a < bind.descriptorCount; a++) { var descriptorBind = bind.binds[a]; ResourceId id = bind.binds[a].res; if (bindMap.arraySize > 1) slotname = String.Format("{0}: {1}[{2}]", bindMap.bind, b.name, a); string name = ""; ulong byteOffset = descriptorBind.offset; ulong length = descriptorBind.size; int numvars = b.variables.Length; if (descriptorBind.res == ResourceId.Null) { name = "Empty"; length = 0; } FetchBuffer buf = m_Core.GetBuffer(id); if (buf != null) { name = buf.name; if (length == ulong.MaxValue) length = buf.length - byteOffset; } if (name == "") name = "UBO " + descriptorBind.res.ToString(); // push constants if (!b.bufferBacked) { setname = ""; slotname = b.name; name = "Push constants"; byteOffset = 0; length = 0; // could maybe get range/size from ShaderVariable.reg if it's filled out // from SPIR-V side. } rows.Add(new object[] { setname, slotname, name, byteOffset, length, numvars, b.byteSize }); } } ExportHTMLTable(writer, new string[] { "Set", "Bind", "Buffer", "Byte Offset", "Byte Size", "Number of Variables", "Bytes Needed" }, rows.ToArray()); } if(shaderDetails.ReadOnlyResources.Length > 0) { writer.WriteStartElement("h3"); writer.WriteString("Read-only Resources"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var b = shaderDetails.ReadOnlyResources[i]; var bindMap = sh.BindpointMapping.ReadOnlyResources[i]; if (!bindMap.used) continue; var set = pipeline.DescSets[sh.BindpointMapping.ReadOnlyResources[i].bindset]; var bind = set.bindings[sh.BindpointMapping.ReadOnlyResources[i].bind]; string setname = bindMap.bindset.ToString(); string slotname = bindMap.bind.ToString(); slotname += ": " + b.name; for (int a = 0; a < bind.descriptorCount; a++) { var descriptorBind = bind.binds[a]; ResourceId id = bind.binds[a].res; if (bindMap.arraySize > 1) slotname = String.Format("{0}: {1}[{2}]", bindMap.bind, b.name, a); string name = ""; if (descriptorBind.res == ResourceId.Null) name = "Empty"; FetchBuffer buf = m_Core.GetBuffer(id); if (buf != null) name = buf.name; FetchTexture tex = m_Core.GetTexture(id); if (tex != null) name = tex.name; if (name == "") name = "Resource " + descriptorBind.res.ToString(); UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 arr = 0; string format = "Unknown"; string viewParams = ""; if(tex != null) { w = tex.width; h = tex.height; d = tex.depth; arr = tex.arraysize; format = tex.format.ToString(); name = tex.name; if (tex.mips > 1) viewParams = String.Format("Mips: {0}-{1}", descriptorBind.baseMip, descriptorBind.baseMip+descriptorBind.numMip - 1); if (tex.arraysize > 1) { if (viewParams.Length > 0) viewParams += ", "; viewParams += String.Format("Layers: {0}-{1}", descriptorBind.baseLayer, descriptorBind.baseLayer + descriptorBind.numLayer - 1); } } if(buf != null) { w = buf.length; h = 0; d = 0; a = 0; format = "-"; name = buf.name; ulong length = descriptorBind.size; if (length == ulong.MaxValue) length = buf.length - descriptorBind.offset; viewParams = String.Format("Byte Range: {0} - {1}", descriptorBind.offset, descriptorBind.offset + length); } if (bind.type != ShaderBindType.Sampler) rows.Add(new object[] { setname, slotname, name, bind.type, w, h, d, arr, format, viewParams }); if (bind.type == ShaderBindType.ImageSampler || bind.type == ShaderBindType.Sampler) { name = "Sampler " + descriptorBind.sampler.ToString(); if (bind.type == ShaderBindType.ImageSampler) setname = slotname = ""; object[] sampDetails = MakeSampler("", "", descriptorBind); rows.Add(new object[] { setname, slotname, name, bind.type, "", "", "", "", sampDetails[5], sampDetails[6] }); } } } ExportHTMLTable(writer, new string[] { "Set", "Bind", "Buffer", "Resource Type", "Width", "Height", "Depth", "Array Size", "Resource Format", "View Parameters" }, rows.ToArray()); } if(shaderDetails.ReadWriteResources.Length > 0) { writer.WriteStartElement("h3"); writer.WriteString("Read-write Resources"); writer.WriteEndElement(); List<object[]> rows = new List<object[]>(); for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var b = shaderDetails.ReadWriteResources[i]; var bindMap = sh.BindpointMapping.ReadWriteResources[i]; if (!bindMap.used) continue; var set = pipeline.DescSets[sh.BindpointMapping.ReadWriteResources[i].bindset]; var bind = set.bindings[sh.BindpointMapping.ReadWriteResources[i].bind]; string setname = bindMap.bindset.ToString(); string slotname = bindMap.bind.ToString(); slotname += ": " + b.name; for (int a = 0; a < bind.descriptorCount; a++) { var descriptorBind = bind.binds[a]; ResourceId id = bind.binds[a].res; if (bindMap.arraySize > 1) slotname = String.Format("{0}: {1}[{2}]", bindMap.bind, b.name, a); string name = ""; if (descriptorBind.res == ResourceId.Null) name = "Empty"; FetchBuffer buf = m_Core.GetBuffer(id); if (buf != null) name = buf.name; FetchTexture tex = m_Core.GetTexture(id); if (tex != null) name = tex.name; if (name == "") name = "Resource " + descriptorBind.res.ToString(); UInt64 w = 1; UInt32 h = 1, d = 1; UInt32 arr = 0; string format = "Unknown"; string viewParams = ""; if (tex != null) { w = tex.width; h = tex.height; d = tex.depth; arr = tex.arraysize; format = tex.format.ToString(); name = tex.name; if (tex.mips > 1) viewParams = String.Format("Highest Mip: {0}", descriptorBind.baseMip); if (tex.arraysize > 1) { if (viewParams.Length > 0) viewParams += ", "; viewParams += String.Format("First array slice: {0}", descriptorBind.baseLayer); } } if (buf != null) { w = buf.length; h = 0; d = 0; a = 0; format = "-"; name = buf.name; ulong length = descriptorBind.size; if (length == ulong.MaxValue) length = buf.length - descriptorBind.offset; viewParams = String.Format("Byte Range: {0} - {1}", descriptorBind.offset, descriptorBind.offset + length); } rows.Add(new object[] { setname, slotname, name, bind.type, w, h, d, arr, format, viewParams }); } } ExportHTMLTable(writer, new string[] { "Set", "Bind", "Buffer", "Resource Type", "Width", "Height", "Depth", "Array Size", "Resource Format", "View Parameters" }, rows.ToArray()); } }
private void AddResourceRow(ShaderReflection shaderDetails, VulkanPipelineState.ShaderStage stage, int bindset, int bind, VulkanPipelineState.Pipeline pipe, TreelistView.TreeListView resources, FetchTexture[] texs, FetchBuffer[] bufs, ref Dictionary<ResourceId, SamplerData> samplers) { ShaderResource shaderRes = null; BindpointMap bindMap = null; bool isrw = false; uint bindPoint = 0; if (shaderDetails != null) { for (int i = 0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind == bind) { bindPoint = (uint)i; shaderRes = ro; bindMap = stage.BindpointMapping.ReadOnlyResources[ro.bindPoint]; } } for (int i = 0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind == bind) { bindPoint = (uint)i; isrw = true; shaderRes = rw; bindMap = stage.BindpointMapping.ReadWriteResources[rw.bindPoint]; } } } VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = null; ShaderBindType bindType = ShaderBindType.Unknown; ShaderStageBits stageBits = (ShaderStageBits)0; if (bindset < pipe.DescSets.Length && bind < pipe.DescSets[bindset].bindings.Length) { slotBinds = pipe.DescSets[bindset].bindings[bind].binds; bindType = pipe.DescSets[bindset].bindings[bind].type; stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; } else { if (shaderRes.IsSampler) bindType = ShaderBindType.Sampler; else if (shaderRes.IsSampler && shaderRes.IsTexture) bindType = ShaderBindType.ImageSampler; else if (shaderRes.resType == ShaderResourceType.Buffer) bindType = ShaderBindType.ReadOnlyTBuffer; else bindType = ShaderBindType.ReadOnlyImage; } bool usedSlot = bindMap != null && bindMap.used; bool stageBitsIncluded = stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage)); // skip descriptors that aren't for this shader stage if (!usedSlot && !stageBitsIncluded) return; if (bindType == ShaderBindType.ConstantBuffer) return; // TODO - check compatibility between bindType and shaderRes.resType ? // consider it filled if any array element is filled bool filledSlot = false; for (int idx = 0; slotBinds != null && idx < slotBinds.Length; idx++) { filledSlot |= slotBinds[idx].res != ResourceId.Null; if (bindType == ShaderBindType.Sampler || bindType == ShaderBindType.ImageSampler) filledSlot |= slotBinds[idx].sampler != ResourceId.Null; } // if it's masked out by stage bits, act as if it's not filled, so it's marked in red if (!stageBitsIncluded) filledSlot = false; // 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" ) { TreelistView.NodeCollection parentNodes = resources.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; int arrayLength = 0; if (slotBinds != null) arrayLength = slotBinds.Length; else arrayLength = (int)bindMap.arraySize; // for arrays, add a parent element that we add the real cbuffers below if (arrayLength > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", arrayLength), "", "", "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < arrayLength; idx++) { VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement descriptorBind = null; if (slotBinds != null) descriptorBind = slotBinds[idx]; if (arrayLength > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; } bool isbuf = false; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; UInt32 samples = 1; UInt64 len = 0; string format = "Unknown"; string name = "Empty"; ShaderResourceType restype = ShaderResourceType.None; object tag = null; bool viewDetails = false; if (filledSlot && descriptorBind != null) { name = "Object " + descriptorBind.res.ToString(); format = descriptorBind.viewfmt.ToString(); // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == descriptorBind.res) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; name = texs[t].name; restype = texs[t].resType; samples = texs[t].msSamp; if (HasImportantViewParams(descriptorBind, texs[t])) viewDetails = true; tag = new ViewTexTag( descriptorBind.viewfmt, descriptorBind.baseMip, descriptorBind.baseLayer, descriptorBind.numMip, descriptorBind.numLayer, texs[t] ); } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == descriptorBind.res) { len = bufs[t].length; w = 0; h = 0; d = 0; a = 0; name = bufs[t].name; restype = ShaderResourceType.Buffer; ulong descriptorLen = descriptorBind.size; if(descriptorLen == ulong.MaxValue) descriptorLen = len - descriptorBind.offset; tag = new BufferResTag(isrw, bindPoint, bufs[t].ID, descriptorBind.offset, descriptorLen); if (HasImportantViewParams(descriptorBind, bufs[t])) viewDetails = true; isbuf = true; } } } else { name = "Empty"; format = "-"; w = h = d = a = 0; } TreelistView.Node node = null; if (bindType == ShaderBindType.ReadWriteBuffer || bindType == ShaderBindType.ReadOnlyTBuffer || bindType == ShaderBindType.ReadWriteTBuffer ) { if (!isbuf) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { string range = "-"; if (descriptorBind != null) range = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.size); node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, name, String.Format("{0} bytes", len), range, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } else if (bindType == ShaderBindType.Sampler) { if (descriptorBind == null || descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { node = parentNodes.Add(MakeSampler(bindset.ToString(), slotname, descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; if (!samplers.ContainsKey(descriptorBind.sampler)) samplers.Add(descriptorBind.sampler, data); } } else { if (descriptorBind == null || descriptorBind.res == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { string typename = restype.Str() + " " + bindType.Str().Replace("&", "&&"); string dim; if (restype == ShaderResourceType.Texture3D) dim = String.Format("{0}x{1}x{2}", w, h, d); else if (restype == ShaderResourceType.Texture1D || restype == ShaderResourceType.Texture1DArray) dim = w.ToString(); else dim = String.Format("{0}x{1}", w, h); if (descriptorBind.swizzle[0] != TextureSwizzle.Red || descriptorBind.swizzle[1] != TextureSwizzle.Green || descriptorBind.swizzle[2] != TextureSwizzle.Blue || descriptorBind.swizzle[3] != TextureSwizzle.Alpha) { format += String.Format(" swizzle[{0}{1}{2}{3}]", descriptorBind.swizzle[0].Str(), descriptorBind.swizzle[1].Str(), descriptorBind.swizzle[2].Str(), descriptorBind.swizzle[3].Str()); } if (restype == ShaderResourceType.Texture1DArray || restype == ShaderResourceType.Texture2DArray || restype == ShaderResourceType.Texture2DMSArray || restype == ShaderResourceType.TextureCubeArray) { dim += String.Format(" {0}[{1}]", restype.Str(), a); } if (restype == ShaderResourceType.Texture2DMS || restype == ShaderResourceType.Texture2DMSArray) dim += String.Format(", {0}x MSAA", samples); node = parentNodes.Add(new object[] { "", bindset, slotname, typename, name, dim, format, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); ViewDetailsRow(node, viewDetails); } if (bindType == ShaderBindType.ImageSampler) { if (descriptorBind == null || descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", }); EmptyRow(node); } else { var texnode = node; if (!samplers.ContainsKey(descriptorBind.sampler)) { node = parentNodes.Add(MakeSampler("", "", descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; samplers.Add(descriptorBind.sampler, data); } if (texnode != null) { m_CombinedImageSamplers[texnode] = samplers[descriptorBind.sampler].node; samplers[descriptorBind.sampler].images.Add(texnode); } } } } } } }
private void ExportHTML(XmlTextWriter writer, VulkanPipelineState.InputAssembly ia) { { writer.WriteStartElement("h3"); writer.WriteString("Index Buffer"); writer.WriteEndElement(); FetchBuffer ib = m_Core.GetBuffer(ia.ibuffer.buf); string name = "Empty"; ulong length = 0; if (ib != null) { name = ib.name; length = ib.length; } string ifmt = "UNKNOWN"; if (m_Core.CurDrawcall.indexByteWidth == 2) ifmt = "UINT16"; if (m_Core.CurDrawcall.indexByteWidth == 4) ifmt = "UINT32"; ExportHTMLTable(writer, new string[] { "Buffer", "Format", "Offset", "Byte Length", "Primitive Restart" }, new object[] { name, ifmt, ia.ibuffer.offs.ToString(), length.ToString(), ia.primitiveRestartEnable ? "Yes" : "No" }); } writer.WriteStartElement("p"); writer.WriteEndElement(); ExportHTMLTable(writer, new string[] { "Primitive Topology", "Tessellation Control Points" }, new object[] { m_Core.CurDrawcall.topology.Str(), m_Core.CurVulkanPipelineState.Tess.numControlPoints }); }
private bool HasImportantViewParams(VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement view, FetchBuffer buf) { if (view.offset > 0 || view.size < buf.length) return true; return false; }
private bool HasImportantViewParams(VulkanPipelineState.CurrentPass.Framebuffer.Attachment att, FetchTexture tex) { // see above in BindingElement overload for justification for comparing formats if (att.viewfmt != tex.format || att.baseMip > 0 || att.baseLayer > 0 || (att.numMip < tex.mips && tex.mips > 1) || (att.numLayer < tex.arraysize && tex.arraysize > 1)) return true; return false; }
private void AddConstantBlockRow(ShaderReflection shaderDetails, VulkanPipelineState.ShaderStage stage, int bindset, int bind, VulkanPipelineState.Pipeline pipe, TreelistView.TreeListView cbuffers, FetchBuffer[] bufs) { ConstantBlock cblock = null; BindpointMap bindMap = null; uint slot = uint.MaxValue; if (shaderDetails != null) { for (slot = 0; slot < (uint)shaderDetails.ConstantBlocks.Length; slot++) { ConstantBlock cb = shaderDetails.ConstantBlocks[slot]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset && stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind == bind) { cblock = cb; bindMap = stage.BindpointMapping.ConstantBlocks[cb.bindPoint]; break; } } if (slot >= (uint)shaderDetails.ConstantBlocks.Length) slot = uint.MaxValue; } VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = null; ShaderBindType bindType = ShaderBindType.ConstantBuffer; ShaderStageBits stageBits = (ShaderStageBits)0; if (bindset < pipe.DescSets.Length && bind < pipe.DescSets[bindset].bindings.Length) { slotBinds = pipe.DescSets[bindset].bindings[bind].binds; bindType = pipe.DescSets[bindset].bindings[bind].type; stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; } bool usedSlot = bindMap != null && bindMap.used; bool stageBitsIncluded = stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage)); // skip descriptors that aren't for this shader stage if (!usedSlot && !stageBitsIncluded) return; if (bindType != ShaderBindType.ConstantBuffer) return; // consider it filled if any array element is filled (or it's push constants) bool filledSlot = cblock != null && !cblock.bufferBacked; for (int idx = 0; slotBinds != null && idx < slotBinds.Length; idx++) filledSlot |= slotBinds[idx].res != ResourceId.Null; // if it's masked out by stage bits, act as if it's not filled, so it's marked in red if (!stageBitsIncluded) filledSlot = false; // 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" ) { TreelistView.NodeCollection parentNodes = cbuffers.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; int arrayLength = 0; if (slotBinds != null) arrayLength = slotBinds.Length; else arrayLength = (int)bindMap.arraySize; // for arrays, add a parent element that we add the real cbuffers below if (arrayLength > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", arrayLength), "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < arrayLength; idx++) { VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement descriptorBind = null; if(slotBinds != null) descriptorBind = slotBinds[idx]; if (arrayLength > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; } string name = "Empty"; UInt64 length = 0; int numvars = cblock != null ? cblock.variables.Length : 0; UInt64 byteSize = cblock != null ? cblock.byteSize : 0; string vecrange = "-"; if (filledSlot && descriptorBind != null) { name = ""; length = descriptorBind.size; for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == descriptorBind.res) { name = bufs[t].name; if(length == ulong.MaxValue) length = bufs[t].length - descriptorBind.offset; } } if (name == "") name = "UBO " + descriptorBind.res.ToString(); vecrange = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.offset + length); } string sizestr; // push constants or specialization constants if (cblock != null && !cblock.bufferBacked) { setname = ""; slotname = cblock.name; name = "Push constants"; vecrange = ""; sizestr = String.Format("{0} Variables", numvars); // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. } else { if (length == byteSize) sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); else sizestr = String.Format("{0} Variables, {1} bytes needed, {2} provided", numvars, byteSize, length); if (length < byteSize) filledSlot = false; } var node = parentNodes.Add(new object[] { "", setname, slotname, name, vecrange, sizestr }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag(slot, (uint)idx); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } }
// when loading a log while replaying remotely, provide the proxy renderer that will be used // as well as the hostname to replay on. public void LoadLogfile(int proxyRenderer, string replayHost, string logFile, bool temporary) { m_LogFile = logFile; m_LogLoadingInProgress = true; if (File.Exists(Core.ConfigFilename)) m_Config.Serialize(Core.ConfigFilename); float postloadProgress = 0.0f; bool progressThread = true; // start a modal dialog to prevent the user interacting with the form while the log is loading. // We'll close it down when log loading finishes (whether it succeeds or fails) ModalPopup modal = new ModalPopup(LogLoadCallback, true); Thread modalThread = Helpers.NewThread(new ThreadStart(() => { modal.SetModalText(string.Format("Loading Log {0}.", m_LogFile)); AppWindow.BeginInvoke(new Action(() => { modal.ShowDialog(AppWindow); })); })); modalThread.Start(); // this thread continually ticks and notifies any threads of the progress, through a float // that is updated by the main loading code Thread thread = Helpers.NewThread(new ThreadStart(() => { modal.LogfileProgressBegin(); foreach (var p in m_ProgressListeners) p.LogfileProgressBegin(); while (progressThread) { Thread.Sleep(2); float progress = 0.8f * m_Renderer.LoadProgress + 0.19f * postloadProgress + 0.01f; modal.LogfileProgress(progress); foreach (var p in m_ProgressListeners) p.LogfileProgress(progress); } })); thread.Start(); // this function call will block until the log is either loaded, or there's some failure m_Renderer.Init(proxyRenderer, replayHost, logFile); // if the renderer isn't running, we hit a failure case so display an error message if (!m_Renderer.Running) { string errmsg = "Unknown error message"; if (m_Renderer.InitException.Data.Contains("status")) errmsg = ((ReplayCreateStatus)m_Renderer.InitException.Data["status"]).Str(); if(proxyRenderer >= 0) MessageBox.Show(String.Format("{0}\nFailed to transfer and replay on remote host {1}: {2}.\n\n" + "Check diagnostic log in Help menu for more details.", logFile, replayHost, errmsg), "Error opening log", MessageBoxButtons.OK, MessageBoxIcon.Error); else MessageBox.Show(String.Format("{0}\nFailed to open logfile for replay: {1}.\n\n" + "Check diagnostic log in Help menu for more details.", logFile, errmsg), "Error opening log", MessageBoxButtons.OK, MessageBoxIcon.Error); progressThread = false; thread.Join(); m_LogLoadingInProgress = false; modal.LogfileProgress(-1.0f); foreach (var p in m_ProgressListeners) p.LogfileProgress(-1.0f); return; } if (!temporary) { m_Config.AddRecentFile(m_Config.RecentLogFiles, logFile, 10); if (File.Exists(Core.ConfigFilename)) m_Config.Serialize(Core.ConfigFilename); } m_FrameID = 0; m_EventID = 0; m_FrameInfo = null; m_APIProperties = null; // fetch initial data like drawcalls, textures and buffers m_Renderer.Invoke((ReplayRenderer r) => { m_FrameInfo = r.GetFrameInfo(); m_APIProperties = r.GetAPIProperties(); postloadProgress = 0.2f; m_DrawCalls = new FetchDrawcall[m_FrameInfo.Length][]; postloadProgress = 0.4f; for (int i = 0; i < m_FrameInfo.Length; i++) m_DrawCalls[i] = FakeProfileMarkers(i, r.GetDrawcalls((UInt32)i)); postloadProgress = 0.7f; m_Buffers = r.GetBuffers(); postloadProgress = 0.8f; var texs = new List<FetchTexture>(r.GetTextures()); m_Textures = texs.OrderBy(o => o.name).ToArray(); postloadProgress = 0.9f; m_D3D11PipelineState = r.GetD3D11PipelineState(); m_GLPipelineState = r.GetGLPipelineState(); m_VulkanPipelineState = r.GetVulkanPipelineState(); m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState, m_VulkanPipelineState); UnreadMessageCount = 0; AddMessages(m_FrameInfo[0].debugMessages); postloadProgress = 1.0f; }); Thread.Sleep(20); DateTime today = DateTime.Now; DateTime compare = today.AddDays(-21); if (compare.CompareTo(Config.DegradedLog_LastUpdate) >= 0 && m_APIProperties.degraded) { Config.DegradedLog_LastUpdate = today; MessageBox.Show(String.Format("{0}\nThis log opened with degraded support - " + "this could mean missing hardware support caused a fallback to software rendering.\n\n" + "This warning will not appear every time this happens, " + "check debug errors/warnings window for more details.", logFile), "Degraded support of log", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } m_LogLoaded = true; progressThread = false; m_LogWatcher = new FileSystemWatcher(Path.GetDirectoryName(m_LogFile), Path.GetFileName(m_LogFile)); m_LogWatcher.EnableRaisingEvents = true; m_LogWatcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite; m_LogWatcher.Created += new FileSystemEventHandler(OnLogfileChanged); m_LogWatcher.Changed += new FileSystemEventHandler(OnLogfileChanged); m_LogWatcher.SynchronizingObject = m_MainWindow; // callbacks on UI thread please List<ILogViewerForm> logviewers = new List<ILogViewerForm>(); logviewers.AddRange(m_LogViewers); // notify all the registers log viewers that a log has been loaded foreach (var logviewer in logviewers) { if (logviewer == null || !(logviewer is Control)) continue; Control c = (Control)logviewer; if (c.InvokeRequired) { if (!c.IsDisposed) { c.Invoke(new Action(() => { try { logviewer.OnLogfileLoaded(); } catch (Exception ex) { throw new AccessViolationException("Rethrown from Invoke:\n" + ex.ToString()); } })); } } else if (!c.IsDisposed) logviewer.OnLogfileLoaded(); } m_LogLoadingInProgress = false; modal.LogfileProgress(1.0f); foreach (var p in m_ProgressListeners) p.LogfileProgress(1.0f); }
private object[] MakeSampler(string bindset, string slotname, VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement descriptor) { string addressing = ""; string addPrefix = ""; string addVal = ""; string filter = ""; string filtPrefix = ""; string filtVal = ""; string[] addr = { descriptor.addrU, descriptor.addrV, descriptor.addrW }; // arrange like either UVW: WRAP or UV: WRAP, W: CLAMP for (int a = 0; a < 3; a++) { string prefix = "" + "UVW"[a]; if (a == 0 || addr[a] == addr[a - 1]) { addPrefix += prefix; } else { addressing += addPrefix + ": " + addVal + ", "; addPrefix = prefix; } addVal = addr[a]; } addressing += addPrefix + ": " + addVal; if (descriptor.borderEnable) addressing += " " + descriptor.border; if (descriptor.unnormalized) addressing += " (Un-norm)"; string[] filters = { descriptor.min, descriptor.mag, descriptor.mip }; string[] filterPrefixes = { "Min", "Mag", "Mip" }; // arrange as addressing above for (int a = 0; a < 3; a++) { if (a == 0 || filters[a] == filters[a - 1]) { if (filtPrefix != "") filtPrefix += "/"; filtPrefix += filterPrefixes[a]; } else { filter += filtPrefix + ": " + filtVal + ", "; filtPrefix = filterPrefixes[a]; } filtVal = filters[a]; } filter += filtPrefix + ": " + filtVal; if (descriptor.maxAniso > 1.0f) filter += String.Format(" Aniso {0}x", descriptor.maxAniso); if (descriptor.compareEnable) filter += String.Format(" ({0})", descriptor.comparison); string lod = "LODs: " + (descriptor.minlod == -float.MaxValue ? "0" : descriptor.minlod.ToString()) + " - " + (descriptor.maxlod == float.MaxValue ? "FLT_MAX" : descriptor.maxlod.ToString()); if (descriptor.mipBias != 0.0f) lod += String.Format(" Bias {0}", descriptor.mipBias); return new object[] { "", bindset, slotname, descriptor.immutableSampler ? "Immutable Sampler" : "Sampler", descriptor.SamplerName, addressing, filter + ", " + lod }; }
private void SetEventID(ILogViewerForm exclude, UInt32 frameID, UInt32 eventID, bool force) { m_FrameID = frameID; m_EventID = eventID; m_DeferredEvent = 0; m_Renderer.Invoke((ReplayRenderer r) => { r.SetContextFilter(ResourceId.Null, 0, 0); }); m_Renderer.Invoke((ReplayRenderer r) => { r.SetFrameEvent(m_FrameID, m_EventID, force); m_D3D11PipelineState = r.GetD3D11PipelineState(); m_GLPipelineState = r.GetGLPipelineState(); m_VulkanPipelineState = r.GetVulkanPipelineState(); m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState, m_VulkanPipelineState); }); foreach (var logviewer in m_LogViewers) { if(logviewer == exclude) continue; Control c = (Control)logviewer; if (c.InvokeRequired) c.Invoke(new Action(() => logviewer.OnEventSelected(frameID, eventID))); else logviewer.OnEventSelected(frameID, eventID); } }
// Set a shader stage's resources and values private void SetShaderState(FetchTexture[] texs, FetchBuffer[] bufs, VulkanPipelineState.ShaderStage stage, VulkanPipelineState.Pipeline pipe, Label shader, TreelistView.TreeListView resources, TreelistView.TreeListView cbuffers) { ShaderReflection shaderDetails = stage.ShaderDetails; if (stage.Shader == ResourceId.Null) shader.Text = "Unbound"; else shader.Text = stage.ShaderName; if (shaderDetails != null && shaderDetails.DebugInfo.entryFunc.Length > 0) { if (shaderDetails.DebugInfo.files.Length > 0 || shaderDetails.DebugInfo.entryFunc != "main") shader.Text = shaderDetails.DebugInfo.entryFunc + "()"; if (shaderDetails.DebugInfo.files.Length > 0) { string shaderfn = ""; int entryFile = shaderDetails.DebugInfo.entryFile; if (entryFile < 0 || entryFile >= shaderDetails.DebugInfo.files.Length) entryFile = 0; shaderfn = shaderDetails.DebugInfo.files[entryFile].BaseFilename; shader.Text += " - " + shaderfn; } } int vs = 0; vs = resources.VScrollValue(); resources.BeginUpdate(); resources.Nodes.Clear(); var samplers = new Dictionary<ResourceId, SamplerData>(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { ShaderResource shaderRes = null; BindpointMap bindMap = null; bool isrw = false; uint bindPoint = 0; if (shaderDetails != null) { for(int i=0; i < shaderDetails.ReadOnlyResources.Length; i++) { var ro = shaderDetails.ReadOnlyResources[i]; if (stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bindset == bindset && stage.BindpointMapping.ReadOnlyResources[ro.bindPoint].bind == bind) { bindPoint = (uint)i; shaderRes = ro; bindMap = stage.BindpointMapping.ReadOnlyResources[ro.bindPoint]; } } for(int i=0; i < shaderDetails.ReadWriteResources.Length; i++) { var rw = shaderDetails.ReadWriteResources[i]; if (stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bindset == bindset && stage.BindpointMapping.ReadWriteResources[rw.bindPoint].bind == bind) { bindPoint = (uint)i; isrw = true; shaderRes = rw; bindMap = stage.BindpointMapping.ReadWriteResources[rw.bindPoint]; } } } VulkanPipelineState.Pipeline.DescriptorSet.DescriptorBinding.BindingElement[] slotBinds = pipe.DescSets[bindset].bindings[bind].binds; ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type; ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; // skip descriptors that aren't for this shader stage if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage))) continue; // these are treated as uniform buffers if (bindType == ShaderBindType.ReadOnlyBuffer) continue; // consider it filled if any array element is filled bool filledSlot = false; for (int idx = 0; idx < slotBinds.Length; idx++) { filledSlot |= slotBinds[idx].res != ResourceId.Null; if(bindType == ShaderBindType.Sampler || bindType == ShaderBindType.ImageSampler) filledSlot |= slotBinds[idx].sampler != ResourceId.Null; } bool usedSlot = bindMap != null && bindMap.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" ) { TreelistView.NodeCollection parentNodes = resources.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if(shaderRes != null) slotname += ": " + shaderRes.name; // for arrays, add a parent element that we add the real cbuffers below if (slotBinds.Length > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "", "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < slotBinds.Length; idx++) { var descriptorBind = slotBinds[idx]; if (slotBinds.Length > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (shaderRes != null && shaderRes.name.Length > 0) slotname += ": " + shaderRes.name; } bool isbuf = false; UInt32 w = 1, h = 1, d = 1; UInt32 a = 1; UInt32 samples = 1; UInt64 len = 0; string format = "Unknown"; string name = "Object " + descriptorBind.res.ToString(); ShaderResourceType restype = ShaderResourceType.None; object tag = null; if (!filledSlot) { name = "Empty"; format = "-"; w = h = d = a = 0; } // check to see if it's a texture for (int t = 0; t < texs.Length; t++) { if (texs[t].ID == descriptorBind.res) { w = texs[t].width; h = texs[t].height; d = texs[t].depth; a = texs[t].arraysize; format = texs[t].format.ToString(); name = texs[t].name; restype = texs[t].resType; samples = texs[t].msSamp; tag = texs[t]; } } // if not a texture, it must be a buffer for (int t = 0; t < bufs.Length; t++) { if (bufs[t].ID == descriptorBind.res) { len = bufs[t].byteSize; w = bufs[t].length; h = 0; d = 0; a = 0; format = ""; name = bufs[t].name; restype = ShaderResourceType.Buffer; tag = new BufferResTag(isrw, bindPoint, bufs[t].ID); isbuf = true; } } TreelistView.Node node = null; if (bindType == ShaderBindType.ReadWriteBuffer || bindType == ShaderBindType.ReadOnlyTBuffer || bindType == ShaderBindType.ReadWriteTBuffer ) { if (!isbuf) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, name, String.Format("{0} bytes", len), String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.size), "", }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } else if (bindType == ShaderBindType.Sampler) { if (descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { node = parentNodes.Add(MakeSampler(bindset.ToString(), slotname, descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; if (!samplers.ContainsKey(descriptorBind.sampler)) samplers.Add(descriptorBind.sampler, data); } } else { if (descriptorBind.res == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { string typename = restype.Str() + " " + bindType.Str().Replace("&", "&&"); string dim; if (restype == ShaderResourceType.Texture3D) dim = String.Format("{0}x{1}x{2}", w, h, d); else if (restype == ShaderResourceType.Texture1D || restype == ShaderResourceType.Texture1DArray) dim = w.ToString(); else dim = String.Format("{0}x{1}", w, h); string arraydim = "-"; if(restype == ShaderResourceType.Texture1DArray || restype == ShaderResourceType.Texture2DArray || restype == ShaderResourceType.Texture2DMSArray || restype == ShaderResourceType.TextureCubeArray) arraydim = String.Format("{0}[{1}]", restype.Str(), a); if (restype == ShaderResourceType.Texture2DMS || restype == ShaderResourceType.Texture2DMSArray) dim += String.Format(", {0}x MSAA", samples); node = parentNodes.Add(new object[] { "", bindset, slotname, typename, name, dim, format, arraydim, }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = tag; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } if (bindType == ShaderBindType.ImageSampler) { if (descriptorBind.sampler == ResourceId.Null) { node = parentNodes.Add(new object[] { "", bindset, slotname, bindType, "-", "-", "", "", }); EmptyRow(node); } else { var texnode = node; if (!samplers.ContainsKey(descriptorBind.sampler)) { node = parentNodes.Add(MakeSampler("", "", descriptorBind)); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); var data = new SamplerData(node); node.Tag = data; samplers.Add(descriptorBind.sampler, data); } if (texnode != null) { m_CombinedImageSamplers[texnode] = samplers[descriptorBind.sampler].node; samplers[descriptorBind.sampler].images.Add(texnode); } } } } } } } } resources.EndUpdate(); resources.NodesSelection.Clear(); resources.SetVScrollValue(vs); vs = cbuffers.VScrollValue(); cbuffers.BeginUpdate(); cbuffers.Nodes.Clear(); for(int bindset = 0; bindset < pipe.DescSets.Length; bindset++) { for(int bind = 0; bind < pipe.DescSets[bindset].bindings.Length; bind++) { ConstantBlock cblock = null; BindpointMap bindMap = null; uint slot = uint.MaxValue; if (shaderDetails != null) { for (slot = 0; slot < (uint)shaderDetails.ConstantBlocks.Length; slot++) { ConstantBlock cb = shaderDetails.ConstantBlocks[slot]; if (stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bindset == bindset && stage.BindpointMapping.ConstantBlocks[cb.bindPoint].bind == bind) { cblock = cb; bindMap = stage.BindpointMapping.ConstantBlocks[cb.bindPoint]; break; } } if (slot >= (uint)shaderDetails.ConstantBlocks.Length) slot = uint.MaxValue; } var slotBinds = pipe.DescSets[bindset].bindings[bind].binds; ShaderBindType bindType = pipe.DescSets[bindset].bindings[bind].type; ShaderStageBits stageBits = pipe.DescSets[bindset].bindings[bind].stageFlags; // skip descriptors that aren't for this shader stage if (!stageBits.HasFlag((ShaderStageBits)(1 << (int)stage.stage))) continue; // these are treated as uniform buffers if (bindType != ShaderBindType.ReadOnlyBuffer) continue; bool usedSlot = bindMap != null && bindMap.used; // consider it filled if any array element is filled (or it's push constants) bool filledSlot = cblock != null && !cblock.bufferBacked; for (int idx = 0; idx < slotBinds.Length; idx++) filledSlot |= slotBinds[idx].res != ResourceId.Null; // 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" ) { TreelistView.NodeCollection parentNodes = cbuffers.Nodes; string setname = bindset.ToString(); string slotname = bind.ToString(); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; // for arrays, add a parent element that we add the real cbuffers below if (slotBinds.Length > 1) { var node = parentNodes.Add(new object[] { "", setname, slotname, String.Format("Array[{0}]", slotBinds.Length), "", "" }); node.TreeColumn = 0; if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); parentNodes = node.Nodes; } for (int idx = 0; idx < slotBinds.Length; idx++) { var descriptorBind = slotBinds[idx]; if (slotBinds.Length > 1) { slotname = String.Format("{0}[{1}]", bind, idx); if (cblock != null && cblock.name.Length > 0) slotname += ": " + cblock.name; } string name = "UBO " + descriptorBind.res.ToString(); UInt64 length = descriptorBind.size; int numvars = cblock != null ? cblock.variables.Length : 0; if (!filledSlot) { name = "Empty"; length = 0; } for (int t = 0; t < bufs.Length; t++) if (bufs[t].ID == descriptorBind.res) name = bufs[t].name; if (name == "") name = "UBO " + descriptorBind.res.ToString(); string sizestr = String.Format("{0} Variables, {1} bytes", numvars, length); string vecrange = String.Format("{0} - {1}", descriptorBind.offset, descriptorBind.offset + descriptorBind.size); // push constants if (cblock != null && !cblock.bufferBacked) { setname = ""; slotname = cblock.name; name = "Push constants"; vecrange = ""; sizestr = String.Format("{0} Variables", numvars); // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. } var node = parentNodes.Add(new object[] { "", setname, slotname, name, vecrange, sizestr }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag(slot, (uint)idx); if (!filledSlot) EmptyRow(node); if (!usedSlot) InactiveRow(node); } } } } // search for push constants and add them last if (shaderDetails != null) { for (int cb = 0; cb < shaderDetails.ConstantBlocks.Length; cb++) { var cblock = shaderDetails.ConstantBlocks[cb]; if (cblock.bufferBacked == false) { // could maybe get range from ShaderVariable.reg if it's filled out // from SPIR-V side. var node = cbuffers.Nodes.Add(new object[] { "", "", cblock.name, "Push constants", "", String.Format("{0} Variables", cblock.variables.Length) }); node.Image = global::renderdocui.Properties.Resources.action; node.HoverImage = global::renderdocui.Properties.Resources.action_hover; node.Tag = new CBufferTag((uint)cb, 0); } } } cbuffers.EndUpdate(); cbuffers.NodesSelection.Clear(); cbuffers.SetVScrollValue(vs); }