Add HTML pipeline export for qrenderdoc

This commit is contained in:
baldurk
2017-05-15 10:11:56 +01:00
parent a6c77b5dee
commit 5b5e30cf54
12 changed files with 3923 additions and 29 deletions
+31
View File
@@ -370,6 +370,17 @@ struct ToStr
return "Unknown";
}
static std::string Get(const QualityHint &el)
{
switch(el)
{
case QualityHint::DontCare: return "Don't Care";
case QualityHint::Nicest: return "Nicest";
case QualityHint::Fastest: return "Fastest";
}
return "Unknown";
}
static std::string Get(const TextureFilter &el)
{
std::string filter = "";
@@ -401,6 +412,26 @@ struct ToStr
return filter;
}
static std::string Get(const D3DBufferViewFlags &el)
{
std::string ret;
if(el == D3DBufferViewFlags::NoFlags)
return "";
if(el & D3DBufferViewFlags::Raw)
ret += " | Raw";
if(el & D3DBufferViewFlags::Append)
ret += " | Append";
if(el & D3DBufferViewFlags::Counter)
ret += " | Counter";
if(!ret.empty())
ret = ret.substr(3);
return ret;
}
static std::string Get(const TextureDim &el)
{
switch(el)
+2
View File
@@ -34,6 +34,8 @@ public:
explicit PipelineFlowChart(QWidget *parent = 0);
~PipelineFlowChart();
const QStringList &stageAbbreviations() { return m_StageAbbrevs; }
const QStringList &stageNames() { return m_StageNames; }
void setStages(const QStringList &abbrevs, const QStringList &names);
void setStageName(int index, const QString &abbrev, const QString &name);
void setIsolatedStage(int index);
@@ -26,6 +26,7 @@
#include <float.h>
#include <QMouseEvent>
#include <QScrollBar>
#include <QXmlStreamWriter>
#include "3rdparty/toolwindowmanager/ToolWindowManager.h"
#include "Code/Resources.h"
#include "PipelineStateViewer.h"
@@ -1034,7 +1035,7 @@ void D3D11PipelineStateViewer::setShaderState(const D3D11Pipe::Shader &stage, QL
if(!filledSlot)
{
name = lit("Empty");
name = tr("Empty");
length = 0;
}
@@ -2294,8 +2295,793 @@ void D3D11PipelineStateViewer::shaderSave_clicked()
m_Common.SaveShaderFile(shaderDetails);
}
QVariantList D3D11PipelineStateViewer::exportViewHTML(D3D11Pipe::View &view, int i,
ShaderReflection *refl,
const QString &extraParams)
{
const ShaderResource *shaderInput = NULL;
bool rw = false;
if(refl)
{
for(const ShaderResource &bind : refl->ReadOnlyResources)
{
if(!bind.IsSampler && bind.bindPoint == i)
{
shaderInput = &bind;
break;
}
}
for(const ShaderResource &bind : refl->ReadWriteResources)
{
if(bind.bindPoint == i)
{
shaderInput = &bind;
rw = true;
break;
}
}
}
QString name = tr("Empty");
QString typeName = tr("Unknown");
QString format = tr("Unknown");
uint64_t w = 1;
uint32_t h = 1, d = 1;
uint32_t a = 0;
QString viewFormat = ToQStr(view.Format.strname);
TextureDescription *tex = m_Ctx.GetTexture(view.Resource);
BufferDescription *buf = m_Ctx.GetBuffer(view.Resource);
QString viewParams;
// check to see if it's a texture
if(tex)
{
w = tex->width;
h = tex->height;
d = tex->depth;
a = tex->arraysize;
format = ToQStr(tex->format.strname);
name = ToQStr(tex->name);
typeName = ToQStr(tex->resType);
if(tex->mips > 1)
viewParams = tr("Highest Mip: %1, Num Mips: %2").arg(view.HighestMip).arg(view.NumMipLevels);
if(tex->arraysize > 1)
{
if(!viewParams.isEmpty())
viewParams += lit(", ");
viewParams +=
tr("First Slice: %1, Array Size: %2").arg(view.FirstArraySlice).arg(view.ArraySize);
}
}
// if not a texture, it must be a buffer
if(buf)
{
w = buf->length;
h = 0;
d = 0;
a = 0;
format = ToQStr(view.Format.strname);
name = ToQStr(buf->name);
typeName = lit("Buffer");
if(view.Flags & D3DBufferViewFlags::Raw)
{
typeName = rw ? lit("RWByteAddressBuffer") : lit("ByteAddressBuffer");
}
else if(view.ElementSize > 0)
{
// for structured buffers, display how many 'elements' there are in the buffer
typeName = QFormatStr("%1[%2]")
.arg(rw ? lit("RWStructuredBuffer") : lit("StructuredBuffer"))
.arg(buf->length / view.ElementSize);
}
if(view.Flags & D3DBufferViewFlags::Append || view.Flags & D3DBufferViewFlags::Counter)
{
typeName += tr(" (Count: %1)").arg(view.BufferStructCount);
}
if(shaderInput && !shaderInput->IsTexture)
{
if(view.Format.compType == CompType::Typeless)
{
if(shaderInput->variableType.members.count > 0)
viewFormat = format = lit("struct ") + ToQStr(shaderInput->variableType.descriptor.name);
else
viewFormat = format = ToQStr(shaderInput->variableType.descriptor.name);
}
else
{
format = ToQStr(view.Format.strname);
}
}
viewParams = tr("First Element: %1, Num Elements %2, Flags %3")
.arg(view.FirstElement)
.arg(view.NumElements)
.arg(ToQStr(view.Flags));
}
if(viewParams.isEmpty())
viewParams = extraParams;
else
viewParams += lit(", ") + extraParams;
return {i, name, ToQStr(view.Type), typeName, w, h, d, a, viewFormat, format, viewParams};
}
void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D11Pipe::IA &ia)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Input Layouts"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D11Pipe::Layout &l : ia.layouts)
{
rows.push_back({i, ToQStr(l.SemanticName), l.SemanticIndex, ToQStr(l.Format.strname),
l.InputSlot, l.ByteOffset, (bool)l.PerInstance, l.InstanceDataStepRate});
i++;
}
m_Common.exportHTMLTable(
xml, {tr("Slot"), tr("Semantic Name"), tr("Semantic Index"), tr("Format"), tr("Input Slot"),
tr("Byte Offset"), tr("Per Instance"), tr("Instance Data Step Rate")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Vertex Buffers"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D11Pipe::VB &vb : ia.vbuffers)
{
QString name = tr("Buffer %1").arg(ToQStr(vb.Buffer));
uint64_t length = 0;
if(vb.Buffer == ResourceId())
{
continue;
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(vb.Buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
}
rows.push_back({i, name, vb.Stride, vb.Offset, length});
i++;
}
m_Common.exportHTMLTable(
xml, {tr("Slot"), tr("Buffer"), tr("Stride"), tr("Offset"), tr("Byte Length")}, rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Index Buffer"));
xml.writeEndElement();
QString name = tr("Buffer %1").arg(ToQStr(ia.ibuffer.Buffer));
uint64_t length = 0;
if(ia.ibuffer.Buffer == ResourceId())
{
name = tr("Empty");
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(ia.ibuffer.Buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
}
QString ifmt = lit("UNKNOWN");
if(m_Ctx.CurDrawcall()->indexByteWidth == 2)
ifmt = lit("R16_UINT");
if(m_Ctx.CurDrawcall()->indexByteWidth == 4)
ifmt = lit("R32_UINT");
m_Common.exportHTMLTable(xml, {tr("Buffer"), tr("Format"), tr("Offset"), tr("Byte Length")},
{name, ifmt, ia.ibuffer.Offset, length});
}
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(xml, {tr("Primitive Topology")}, {ToQStr(m_Ctx.CurDrawcall()->topology)});
}
void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D11Pipe::Shader &sh)
{
ShaderReflection *shaderDetails = sh.ShaderDetails;
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Shader"));
xml.writeEndElement();
QString shadername = tr("Unknown");
if(sh.Object == ResourceId())
shadername = tr("Unbound");
else
shadername = ToQStr(sh.name);
if(shaderDetails && shaderDetails->DebugInfo.entryFunc.count > 0 &&
shaderDetails->DebugInfo.files.count > 0)
{
QString shaderfn;
int entryFile = shaderDetails->DebugInfo.entryFile;
if(entryFile < 0 || entryFile >= shaderDetails->DebugInfo.files.count)
entryFile = 0;
shaderfn = QFileInfo(ToQStr(shaderDetails->DebugInfo.files[entryFile].first)).fileName();
shadername =
QFormatStr("%1() - %2").arg(ToQStr(shaderDetails->DebugInfo.entryFunc)).arg(shaderfn);
}
xml.writeStartElement(lit("p"));
xml.writeCharacters(shadername);
xml.writeEndElement();
if(sh.Object == ResourceId())
return;
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Resources"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < sh.SRVs.count; i++)
{
if(sh.SRVs[i].Object == ResourceId())
continue;
rows.push_back(exportViewHTML(sh.SRVs[i], i, shaderDetails, QString()));
}
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Name"), tr("View Type"), tr("Resource Type"),
tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters"),
},
rows);
}
if(sh.stage == ShaderStage::Compute)
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Unordered Access Views"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < sh.UAVs.count; i++)
{
if(sh.UAVs[i].Object == ResourceId())
continue;
rows.push_back(exportViewHTML(sh.UAVs[i], i, shaderDetails, QString()));
}
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Name"), tr("View Type"), tr("Resource Type"),
tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters"),
},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Samplers"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < sh.Samplers.count; i++)
{
const D3D11Pipe::Sampler &s = sh.Samplers[i];
if(s.Samp == ResourceId())
continue;
QString borderColor = QFormatStr("%1, %2, %3, %4")
.arg(s.BorderColor[0])
.arg(s.BorderColor[1])
.arg(s.BorderColor[2])
.arg(s.BorderColor[3]);
QString addressing;
QString addPrefix;
QString addVal;
QString addr[] = {ToQStr(s.AddressU), ToQStr(s.AddressV), ToQStr(s.AddressW)};
// arrange like either UVW: WRAP or UV: WRAP, W: CLAMP
for(int a = 0; a < 3; a++)
{
const QString str[] = {lit("U"), lit("V"), lit("W")};
QString prefix = str[a];
if(a == 0 || addr[a] == addr[a - 1])
{
addPrefix += prefix;
}
else
{
addressing += QFormatStr("%1: %2, ").arg(addPrefix).arg(addVal);
addPrefix = prefix;
}
addVal = addr[a];
}
addressing += addPrefix + lit(": ") + addVal;
rows.push_back({i, addressing, borderColor, ToQStr(s.Comparison), ToQStr(s.Filter),
s.MaxAniso, s.MinLOD == -FLT_MAX ? lit("0") : QString::number(s.MinLOD),
s.MaxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(s.MaxLOD), s.MipLODBias});
}
m_Common.exportHTMLTable(
xml,
{
tr("Slot"), tr("Addressing"), tr("Border Colour"), tr("Comparison"), tr("Filter"),
tr("Max Anisotropy"), tr("Min LOD"), tr("Max LOD"), tr("Mip Bias"),
},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Constant Buffers"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < sh.ConstantBuffers.count; i++)
{
ConstantBlock *shaderCBuf = NULL;
if(sh.ConstantBuffers[i].Buffer == ResourceId())
continue;
if(shaderDetails && i < shaderDetails->ConstantBlocks.count &&
shaderDetails->ConstantBlocks[i].name.count > 0)
shaderCBuf = &shaderDetails->ConstantBlocks[i];
QString name = tr("Constant Buffer %1").arg(ToQStr(sh.ConstantBuffers[i].Buffer));
uint64_t length = 1;
int numvars = shaderCBuf ? shaderCBuf->variables.count : 0;
uint32_t byteSize = shaderCBuf ? shaderCBuf->byteSize : 0;
if(sh.ConstantBuffers[i].Buffer == ResourceId())
{
name = tr("Empty");
length = 0;
}
BufferDescription *buf = m_Ctx.GetBuffer(sh.ConstantBuffers[i].Buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
rows.push_back({i, name, sh.ConstantBuffers[i].VecOffset, sh.ConstantBuffers[i].VecCount,
numvars, byteSize, length});
}
m_Common.exportHTMLTable(xml,
{tr("Slot"), tr("Buffer"), tr("Vector Offset"), tr("Vector Count"),
tr("Number of Variables"), tr("Bytes Needed"), tr("Bytes Provided")},
rows);
}
if(sh.ClassInstances.count > 0)
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Class Instances"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < sh.ClassInstances.count; i++)
{
QString interfaceName = tr("Interface %1").arg(i);
if(sh.ShaderDetails && i < sh.ShaderDetails->Interfaces.count)
interfaceName = ToQStr(sh.ShaderDetails->Interfaces[i]);
rows.push_back({i, interfaceName, ToQStr(sh.ClassInstances[i])});
}
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Interface Name"), tr("Instance Name"),
},
rows);
}
}
void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D11Pipe::SO &so)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Stream Out Targets"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D11Pipe::SOBind &o : so.Outputs)
{
QString name = tr("Buffer %1").arg(ToQStr(o.Buffer));
uint64_t length = 0;
if(o.Buffer == ResourceId())
{
name = tr("Empty");
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(o.Buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
}
rows.push_back({i, name, o.Offset, length});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("Buffer"), tr("Offset"), tr("Byte Length")}, rows);
}
}
void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D11Pipe::Rasterizer &rs)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("States"));
xml.writeEndElement();
m_Common.exportHTMLTable(xml, {tr("Fill Mode"), tr("Cull Mode"), tr("Front CCW")},
{ToQStr(rs.m_State.fillMode), ToQStr(rs.m_State.cullMode),
rs.m_State.FrontCCW ? tr("Yes") : tr("No")});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Scissor Enable"), tr("Line AA Enable"), tr("Multisample Enable"),
tr("Forced Sample Count"), tr("Conservative Raster")},
{rs.m_State.ScissorEnable ? tr("Yes") : tr("No"),
rs.m_State.AntialiasedLineEnable ? tr("Yes") : tr("No"),
rs.m_State.MultisampleEnable ? tr("Yes") : tr("No"), rs.m_State.ForcedSampleCount,
rs.m_State.ConservativeRasterization ? tr("Yes") : tr("No")});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Depth Clip"), tr("Depth Bias"), tr("Depth Bias Clamp"), tr("Slope Scaled Bias")},
{rs.m_State.DepthClip ? tr("Yes") : tr("No"), rs.m_State.DepthBias,
Formatter::Format(rs.m_State.DepthBiasClamp),
Formatter::Format(rs.m_State.SlopeScaledDepthBias)});
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Viewports"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D11Pipe::Viewport &v : rs.Viewports)
{
if(v.Width == v.Height && v.Width == 0 && v.Height == 0)
continue;
rows.push_back({i, v.X, v.Y, v.Width, v.Height, v.MinDepth, v.MaxDepth});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("X"), tr("Y"), tr("Width"), tr("Height"),
tr("Min Depth"), tr("Max Depth")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Scissors"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D11Pipe::Scissor &s : rs.Scissors)
{
if(s.right == 0 && s.bottom == 0)
continue;
rows.push_back({i, s.left, s.top, s.right - s.left, s.bottom - s.top});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("X"), tr("Y"), tr("Width"), tr("Height")}, rows);
}
}
void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D11Pipe::OM &om)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Blend State"));
xml.writeEndElement();
QString blendFactor = QFormatStr("%1, %2, %3, %4")
.arg(om.m_BlendState.BlendFactor[0], 0, 'f', 2)
.arg(om.m_BlendState.BlendFactor[1], 0, 'f', 2)
.arg(om.m_BlendState.BlendFactor[2], 0, 'f', 2)
.arg(om.m_BlendState.BlendFactor[3], 0, 'f', 2);
m_Common.exportHTMLTable(xml, {tr("Independent Blend Enable"), tr("Alpha to Coverage"),
tr("Sample Mask"), tr("Blend Factor")},
{
om.m_BlendState.IndependentBlend ? tr("Yes") : tr("No"),
om.m_BlendState.AlphaToCoverage ? tr("Yes") : tr("No"),
Formatter::Format(om.m_BlendState.SampleMask, true), blendFactor,
});
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Target Blends"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D11Pipe::Blend &b : om.m_BlendState.Blends)
{
if(i >= om.RenderTargets.count)
continue;
QString mask = QFormatStr("%1%2%3%4")
.arg((b.WriteMask & 0x1) == 0 ? lit("_") : lit("R"))
.arg((b.WriteMask & 0x2) == 0 ? lit("_") : lit("G"))
.arg((b.WriteMask & 0x4) == 0 ? lit("_") : lit("B"))
.arg((b.WriteMask & 0x8) == 0 ? lit("_") : lit("A"));
rows.push_back({i, b.Enabled ? tr("Yes") : tr("No"), b.LogicEnabled ? tr("Yes") : tr("No"),
ToQStr(b.m_Blend.Source), ToQStr(b.m_Blend.Destination),
ToQStr(b.m_Blend.Operation), ToQStr(b.m_AlphaBlend.Source),
ToQStr(b.m_AlphaBlend.Destination), ToQStr(b.m_AlphaBlend.Operation),
ToQStr(b.Logic), mask});
i++;
}
m_Common.exportHTMLTable(
xml,
{
tr("Slot"), tr("Blend Enable"), tr("Logic Enable"), tr("Blend Source"),
tr("Blend Destination"), tr("Blend Operation"), tr("Alpha Blend Source"),
tr("Alpha Blend Destination"), tr("Alpha Blend Operation"), tr("Logic Operation"),
tr("Write Mask"),
},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Depth State"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Depth Test Enable"), tr("Depth Writes Enable"), tr("Depth Function")},
{om.m_State.DepthEnable ? tr("Yes") : tr("No"),
om.m_State.DepthWrites ? tr("Yes") : tr("No"), ToQStr(om.m_State.DepthFunc)});
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Stencil State"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Stencil Test Enable"), tr("Stencil Read Mask"), tr("Stencil Write Mask")},
{om.m_State.StencilEnable ? tr("Yes") : tr("No"),
Formatter::Format(om.m_State.StencilReadMask, true),
Formatter::Format(om.m_State.StencilWriteMask, true)});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Face"), tr("Function"), tr("Pass Operation"), tr("Fail Operation"),
tr("Depth Fail Operation")},
{
{tr("Front"), ToQStr(om.m_State.m_FrontFace.Func), ToQStr(om.m_State.m_FrontFace.PassOp),
ToQStr(om.m_State.m_FrontFace.FailOp), ToQStr(om.m_State.m_FrontFace.DepthFailOp)},
{tr("Back"), ToQStr(om.m_State.m_BackFace.Func), ToQStr(om.m_State.m_BackFace.PassOp),
ToQStr(om.m_State.m_BackFace.FailOp), ToQStr(om.m_State.m_BackFace.DepthFailOp)},
});
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Render targets"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < om.RenderTargets.count; i++)
{
if(om.RenderTargets[i].Object == ResourceId())
continue;
rows.push_back(exportViewHTML(om.RenderTargets[i], i, NULL, QString()));
}
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Name"), tr("View Type"), tr("Resource Type"),
tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters"),
},
rows);
}
if(om.UAVs.count > 0 && om.UAVs[0].Object != ResourceId())
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Unordered Access Views"));
xml.writeEndElement();
QList<QVariantList> rows;
uint32_t i = 0;
for(; i < om.UAVStartSlot; i++)
rows.push_back({i, tr("Empty"), QString(), QString(), QString(), QString(), 0, 0, 0, 0,
QString(), QString(), QString()});
for(; i < (uint32_t)om.RenderTargets.count; i++)
{
if(om.UAVs[i - om.UAVStartSlot].Object == ResourceId())
continue;
rows.push_back(exportViewHTML(om.UAVs[i - om.UAVStartSlot], i,
m_Ctx.CurD3D11PipelineState().m_PS.ShaderDetails, QString()));
}
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Name"), tr("View Type"), tr("Resource Type"),
tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters"),
},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Depth target"));
xml.writeEndElement();
QList<QVariantList> rows;
QString extra;
if(om.DepthReadOnly && om.StencilReadOnly)
extra = tr("Depth & Stencil Read-Only");
else if(om.DepthReadOnly)
extra = tr("Depth Read-Only");
else if(om.StencilReadOnly)
extra = tr("Stencil Read-Only");
rows.push_back(exportViewHTML(om.DepthTarget, 0, NULL, extra));
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Name"), tr("View Type"), tr("Resource Type"),
tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters"),
},
rows);
}
}
void D3D11PipelineStateViewer::on_exportHTML_clicked()
{
QXmlStreamWriter *xmlptr = m_Common.beginHTMLExport();
if(xmlptr)
{
QXmlStreamWriter &xml = *xmlptr;
const QStringList &stageNames = ui->pipeFlow->stageNames();
const QStringList &stageAbbrevs = ui->pipeFlow->stageAbbreviations();
int stage = 0;
for(const QString &sn : stageNames)
{
xml.writeStartElement(lit("div"));
xml.writeStartElement(lit("a"));
xml.writeAttribute(lit("name"), stageAbbrevs[stage]);
xml.writeEndElement();
xml.writeEndElement();
xml.writeStartElement(lit("div"));
xml.writeAttribute(lit("class"), lit("stage"));
xml.writeStartElement(lit("h1"));
xml.writeCharacters(sn);
xml.writeEndElement();
switch(stage)
{
case 0: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_IA); break;
case 1: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_VS); break;
case 2: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_HS); break;
case 3: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_DS); break;
case 4:
exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_GS);
exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_SO);
break;
case 5: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_RS); break;
case 6: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_PS); break;
case 7: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_OM); break;
case 8: exportHTML(xml, m_Ctx.CurD3D11PipelineState().m_CS); break;
}
xml.writeEndElement();
stage++;
}
m_Common.endHTMLExport(xmlptr);
}
}
void D3D11PipelineStateViewer::on_meshView_clicked()
@@ -32,6 +32,7 @@ namespace Ui
class D3D11PipelineStateViewer;
}
class QXmlStreamWriter;
class RDTreeWidget;
class RDTreeWidgetItem;
struct D3D11ViewTag;
@@ -92,6 +93,14 @@ private:
void setState();
void clearState();
QVariantList exportViewHTML(D3D11Pipe::View &view, int i, ShaderReflection *refl,
const QString &extraParams);
void exportHTML(QXmlStreamWriter &xml, D3D11Pipe::IA &ia);
void exportHTML(QXmlStreamWriter &xml, D3D11Pipe::Shader &sh);
void exportHTML(QXmlStreamWriter &xml, D3D11Pipe::SO &so);
void exportHTML(QXmlStreamWriter &xml, D3D11Pipe::Rasterizer &rs);
void exportHTML(QXmlStreamWriter &xml, D3D11Pipe::OM &om);
void setInactiveRow(RDTreeWidgetItem *node);
void setEmptyRow(RDTreeWidgetItem *node);
void highlightIABind(int slot);
@@ -26,6 +26,7 @@
#include <float.h>
#include <QMouseEvent>
#include <QScrollBar>
#include <QXmlStreamWriter>
#include "3rdparty/toolwindowmanager/ToolWindowManager.h"
#include "Code/Resources.h"
#include "PipelineStateViewer.h"
@@ -939,9 +940,8 @@ void D3D12PipelineStateViewer::setShaderState(const D3D12Pipe::Shader &stage, QL
shader->setText(
QFormatStr("%1 - %2").arg(ToQStr(state.name)).arg(m_Ctx.CurPipelineState().Abbrev(stage.stage)));
else
shader->setText(QFormatStr("%1 - %2 Shader")
.arg(ToQStr(state.name))
.arg(ToQStr(stage.stage, GraphicsAPI::D3D12)));
shader->setText(
tr("%1 - %2 Shader").arg(ToQStr(state.name)).arg(ToQStr(stage.stage, GraphicsAPI::D3D12)));
if(shaderDetails && !shaderDetails->DebugInfo.entryFunc.empty() &&
!shaderDetails->DebugInfo.files.empty())
@@ -1184,7 +1184,7 @@ void D3D12PipelineStateViewer::setShaderState(const D3D12Pipe::Shader &stage, QL
bytesize = uint32_t(b.RootValues.count * 4);
if(!filledSlot)
name = lit("Empty");
name = tr("Empty");
BufferDescription *buf = m_Ctx.GetBuffer(b.Buffer);
@@ -2181,8 +2181,930 @@ void D3D12PipelineStateViewer::shaderSave_clicked()
m_Common.SaveShaderFile(shaderDetails);
}
QVariantList D3D12PipelineStateViewer::exportViewHTML(const D3D12Pipe::View &view, bool rw,
const ShaderResource *shaderInput,
const QString &extraParams)
{
QString name = tr("Empty");
QString typeName = tr("Unknown");
QString format = tr("Unknown");
uint64_t w = 1;
uint32_t h = 1, d = 1;
uint32_t a = 0;
QString viewFormat = ToQStr(view.Format.strname);
TextureDescription *tex = m_Ctx.GetTexture(view.Resource);
BufferDescription *buf = m_Ctx.GetBuffer(view.Resource);
QString viewParams;
// check to see if it's a texture
if(tex)
{
w = tex->width;
h = tex->height;
d = tex->depth;
a = tex->arraysize;
format = ToQStr(tex->format.strname);
name = ToQStr(tex->name);
typeName = ToQStr(tex->resType);
if(view.swizzle[0] != TextureSwizzle::Red || view.swizzle[1] != TextureSwizzle::Green ||
view.swizzle[2] != TextureSwizzle::Blue || view.swizzle[3] != TextureSwizzle::Alpha)
{
format += tr(" swizzle[%1%2%3%4]")
.arg(ToQStr(view.swizzle[0]))
.arg(ToQStr(view.swizzle[1]))
.arg(ToQStr(view.swizzle[2]))
.arg(ToQStr(view.swizzle[3]));
}
if(tex->mips > 1)
viewParams = tr("Highest Mip: %1, Num Mips: %2").arg(view.HighestMip).arg(view.NumMipLevels);
if(tex->arraysize > 1)
{
if(!viewParams.isEmpty())
viewParams += lit(", ");
viewParams +=
tr("First Slice: %1, Array Size: %2").arg(view.FirstArraySlice).arg(view.ArraySize);
}
if(view.MinLODClamp > 0.0f)
{
if(!viewParams.isEmpty())
viewParams += lit(", ");
viewParams += tr("MinLODClamp: %1").arg(view.MinLODClamp);
}
}
// if not a texture, it must be a buffer
if(buf)
{
w = buf->length;
h = 0;
d = 0;
a = 0;
format = ToQStr(view.Format.strname);
name = ToQStr(buf->name);
typeName = lit("Buffer");
if(view.BufferFlags & D3DBufferViewFlags::Raw)
{
typeName = rw ? lit("RWByteAddressBuffer") : lit("ByteAddressBuffer");
}
else if(view.ElementSize > 0)
{
// for structured buffers, display how many 'elements' there are in the buffer
typeName = QFormatStr("%1[%2]")
.arg(rw ? lit("RWStructuredBuffer") : lit("StructuredBuffer"))
.arg(buf->length / view.ElementSize);
}
if(view.BufferFlags & D3DBufferViewFlags::Append || view.BufferFlags & D3DBufferViewFlags::Counter)
{
typeName += tr(" (Count: %1)").arg(view.BufferStructCount);
}
if(shaderInput && !shaderInput->IsTexture)
{
if(view.Format.compType == CompType::Typeless)
{
if(shaderInput->variableType.members.count > 0)
viewFormat = format = lit("struct ") + ToQStr(shaderInput->variableType.descriptor.name);
else
viewFormat = format = ToQStr(shaderInput->variableType.descriptor.name);
}
else
{
format = ToQStr(view.Format.strname);
}
}
viewParams = tr("First Element: %1, Num Elements %2, Flags %3")
.arg(view.FirstElement)
.arg(view.NumElements)
.arg(ToQStr(view.BufferFlags));
if(view.CounterResource != ResourceId())
{
QString counterName = tr("Buffer %1").arg(ToQStr(view.CounterResource));
BufferDescription *counterBuf = m_Ctx.GetBuffer(view.CounterResource);
if(counterBuf)
counterName = ToQStr(counterBuf->name);
viewParams += tr(", Counter in %1 at %2 bytes").arg(counterName).arg(view.CounterByteOffset);
}
}
if(viewParams.isEmpty())
viewParams = extraParams;
else
viewParams += lit(", ") + extraParams;
return {name, ToQStr(view.Type), typeName, w, h, d, a, viewFormat, format, viewParams};
}
void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D12Pipe::IA &ia)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Input Layouts"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D12Pipe::Layout &l : ia.layouts)
{
rows.push_back({i, ToQStr(l.SemanticName), l.SemanticIndex, ToQStr(l.Format.strname),
l.InputSlot, l.ByteOffset, (bool)l.PerInstance, l.InstanceDataStepRate});
i++;
}
m_Common.exportHTMLTable(
xml, {tr("Slot"), tr("Semantic Name"), tr("Semantic Index"), tr("Format"), tr("Input Slot"),
tr("Byte Offset"), tr("Per Instance"), tr("Instance Data Step Rate")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Vertex Buffers"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D12Pipe::VB &vb : ia.vbuffers)
{
QString name = tr("Buffer %1").arg(ToQStr(vb.Buffer));
uint64_t length = 0;
if(vb.Buffer == ResourceId())
{
continue;
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(vb.Buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
}
length = qMin(length, (uint64_t)vb.Size);
rows.push_back({i, name, vb.Stride, vb.Offset, length});
i++;
}
m_Common.exportHTMLTable(
xml, {tr("Slot"), tr("Buffer"), tr("Stride"), tr("Offset"), tr("Byte Length")}, rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Index Buffer"));
xml.writeEndElement();
QString name = tr("Buffer %1").arg(ToQStr(ia.ibuffer.Buffer));
uint64_t length = 0;
if(ia.ibuffer.Buffer == ResourceId())
{
name = tr("Empty");
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(ia.ibuffer.Buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
}
length = qMin(length, (uint64_t)ia.ibuffer.Size);
QString ifmt = lit("UNKNOWN");
if(m_Ctx.CurDrawcall()->indexByteWidth == 2)
ifmt = lit("R16_UINT");
if(m_Ctx.CurDrawcall()->indexByteWidth == 4)
ifmt = lit("R32_UINT");
m_Common.exportHTMLTable(xml, {tr("Buffer"), tr("Format"), tr("Offset"), tr("Byte Length")},
{name, ifmt, ia.ibuffer.Offset, length});
}
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(xml, {tr("Primitive Topology")}, {ToQStr(m_Ctx.CurDrawcall()->topology)});
}
void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D12Pipe::Shader &sh)
{
ShaderReflection *shaderDetails = sh.ShaderDetails;
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Shader"));
xml.writeEndElement();
QString shadername = tr("Unknown");
const D3D12Pipe::State &state = m_Ctx.CurD3D12PipelineState();
if(sh.Object == ResourceId())
shadername = tr("Unbound");
else if(state.customName)
shadername =
QFormatStr("%1 - %2").arg(ToQStr(state.name)).arg(m_Ctx.CurPipelineState().Abbrev(sh.stage));
else
shadername =
tr("%1 - %2 Shader").arg(ToQStr(state.name)).arg(ToQStr(sh.stage, GraphicsAPI::D3D12));
if(shaderDetails && !shaderDetails->DebugInfo.entryFunc.empty() &&
!shaderDetails->DebugInfo.files.empty())
{
QString shaderfn;
int entryFile = shaderDetails->DebugInfo.entryFile;
if(entryFile < 0 || entryFile >= shaderDetails->DebugInfo.files.count)
entryFile = 0;
shaderfn = QFileInfo(ToQStr(shaderDetails->DebugInfo.files[entryFile].first)).fileName();
shadername =
QFormatStr("%1() - %2").arg(ToQStr(shaderDetails->DebugInfo.entryFunc)).arg(shaderfn);
}
xml.writeStartElement(lit("p"));
xml.writeCharacters(shadername);
xml.writeEndElement();
if(sh.Object == ResourceId())
return;
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Shader Resource Views"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int space = 0; space < sh.Spaces.count; space++)
{
for(int reg = 0; reg < sh.Spaces[space].SRVs.count; reg++)
{
const D3D12Pipe::View &v = sh.Spaces[space].SRVs[reg];
// consider this register to not exist - it's in a gap defined by sparse root signature
// elements
if(v.RootElement == ~0U)
continue;
const ShaderResource *shaderInput = NULL;
if(sh.ShaderDetails)
{
for(int i = 0; i < sh.BindpointMapping.ReadOnlyResources.count; i++)
{
const BindpointMap &b = sh.BindpointMapping.ReadOnlyResources[i];
const ShaderResource &res = sh.ShaderDetails->ReadOnlyResources[i];
bool regMatch = b.bind == reg;
// handle unbounded arrays specially. It's illegal to have an unbounded array with
// anything after it
if(b.bind <= reg)
regMatch = (b.arraySize == ~0U) || (b.bind + (int)b.arraySize > reg);
if(b.bindset == space && regMatch && !res.IsReadOnly && !res.IsSampler)
{
shaderInput = &res;
break;
}
}
}
QString rootel = v.Immediate ? tr("#%1 Direct").arg(v.RootElement)
: tr("#%1 Table[%2]").arg(v.RootElement).arg(v.TableIndex);
QVariantList row = exportViewHTML(v, false, shaderInput, QString());
row.push_front(reg);
row.push_front(space);
row.push_front(rootel);
rows.push_back(row);
}
}
m_Common.exportHTMLTable(
xml, {tr("Root Sig El"), tr("Space"), tr("Register"), tr("Resource"), tr("View Type"),
tr("Resource Type"), tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Unordered Access Views"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int space = 0; space < sh.Spaces.count; space++)
{
for(int reg = 0; reg < sh.Spaces[space].UAVs.count; reg++)
{
const D3D12Pipe::View &v = sh.Spaces[space].UAVs[reg];
// consider this register to not exist - it's in a gap defined by sparse root signature
// elements
if(v.RootElement == ~0U)
continue;
const ShaderResource *shaderInput = NULL;
if(sh.ShaderDetails)
{
for(int i = 0; i < sh.BindpointMapping.ReadOnlyResources.count; i++)
{
const BindpointMap &b = sh.BindpointMapping.ReadOnlyResources[i];
const ShaderResource &res = sh.ShaderDetails->ReadOnlyResources[i];
bool regMatch = b.bind == reg;
// handle unbounded arrays specially. It's illegal to have an unbounded array with
// anything after it
if(b.bind <= reg)
regMatch = (b.arraySize == ~0U) || (b.bind + (int)b.arraySize > reg);
if(b.bindset == space && regMatch && !res.IsReadOnly && !res.IsSampler)
{
shaderInput = &res;
break;
}
}
}
QString rootel = v.Immediate ? tr("#%1 Direct").arg(v.RootElement)
: tr("#%1 Table[%2]").arg(v.RootElement).arg(v.TableIndex);
QVariantList row = exportViewHTML(v, true, shaderInput, QString());
row.push_front(reg);
row.push_front(space);
row.push_front(rootel);
rows.push_back(row);
}
}
m_Common.exportHTMLTable(
xml, {tr("Root Sig El"), tr("Space"), tr("Register"), tr("Resource"), tr("View Type"),
tr("Resource Type"), tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Samplers"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int space = 0; space < sh.Spaces.count; space++)
{
for(int reg = 0; reg < sh.Spaces[space].Samplers.count; reg++)
{
const D3D12Pipe::Sampler &s = sh.Spaces[space].Samplers[reg];
// consider this register to not exist - it's in a gap defined by sparse root signature
// elements
if(s.RootElement == ~0U)
continue;
const ShaderResource *shaderInput = NULL;
if(sh.ShaderDetails)
{
for(int i = 0; i < sh.BindpointMapping.ReadOnlyResources.count; i++)
{
const BindpointMap &b = sh.BindpointMapping.ReadOnlyResources[i];
const ShaderResource &res = sh.ShaderDetails->ReadOnlyResources[i];
bool regMatch = b.bind == reg;
// handle unbounded arrays specially. It's illegal to have an unbounded array with
// anything after it
if(b.bind <= reg)
regMatch = (b.arraySize == ~0U) || (b.bind + (int)b.arraySize > reg);
if(b.bindset == space && regMatch && res.IsSampler)
{
shaderInput = &res;
break;
}
}
}
QString rootel = s.Immediate ? tr("#%1 Static").arg(s.RootElement)
: tr("#%1 Table[%2]").arg(s.RootElement).arg(s.TableIndex);
{
QString regname = QString::number(reg);
if(shaderInput && !shaderInput->name.empty())
regname += lit(": ") + ToQStr(shaderInput->name);
QString borderColor = QFormatStr("%1, %2, %3, %4")
.arg(s.BorderColor[0])
.arg(s.BorderColor[1])
.arg(s.BorderColor[2])
.arg(s.BorderColor[3]);
QString addressing;
QString addPrefix;
QString addVal;
QString addr[] = {ToQStr(s.AddressU), ToQStr(s.AddressV), ToQStr(s.AddressW)};
// arrange like either UVW: WRAP or UV: WRAP, W: CLAMP
for(int a = 0; a < 3; a++)
{
const QString str[] = {lit("U"), lit("V"), lit("W")};
QString prefix = str[a];
if(a == 0 || addr[a] == addr[a - 1])
{
addPrefix += prefix;
}
else
{
addressing += QFormatStr("%1: %2, ").arg(addPrefix).arg(addVal);
addPrefix = prefix;
}
addVal = addr[a];
}
addressing += addPrefix + lit(": ") + addVal;
if(s.UseBorder())
addressing += QFormatStr("<%1>").arg(borderColor);
QString filter = ToQStr(s.Filter);
if(s.MaxAniso > 1)
filter += QFormatStr(" %1x").arg(s.MaxAniso);
if(s.Filter.func == FilterFunc::Comparison)
filter += QFormatStr(" (%1)").arg(ToQStr(s.Comparison));
else if(s.Filter.func != FilterFunc::Normal)
filter += QFormatStr(" (%1)").arg(ToQStr(s.Filter.func));
rows.push_back({rootel, space, regname, addressing, filter,
QFormatStr("%1 - %2")
.arg(s.MinLOD == -FLT_MAX ? lit("0") : QString::number(s.MinLOD))
.arg(s.MaxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(s.MaxLOD)),
s.MipLODBias});
}
}
}
m_Common.exportHTMLTable(xml, {tr("Root Sig El"), tr("Space"), tr("Register"), tr("Addressing"),
tr("Filter"), tr("LOD Clamp"), tr("LOD Bias")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Constant Buffers"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int space = 0; space < sh.Spaces.count; space++)
{
for(int reg = 0; reg < sh.Spaces[space].ConstantBuffers.count; reg++)
{
const D3D12Pipe::CBuffer &b = sh.Spaces[space].ConstantBuffers[reg];
const ConstantBlock *shaderCBuf = NULL;
if(sh.ShaderDetails)
{
for(int i = 0; i < sh.BindpointMapping.ConstantBlocks.count; i++)
{
const BindpointMap &bm = sh.BindpointMapping.ConstantBlocks[i];
const ConstantBlock &res = sh.ShaderDetails->ConstantBlocks[i];
bool regMatch = bm.bind == reg;
// handle unbounded arrays specially. It's illegal to have an unbounded array with
// anything after it
if(bm.bind <= reg)
regMatch = (bm.arraySize == ~0U) || (bm.bind + (int)bm.arraySize > reg);
if(bm.bindset == space && regMatch)
{
shaderCBuf = &res;
break;
}
}
}
QString rootel;
if(b.Immediate)
{
if(!b.RootValues.empty())
rootel = tr("#%1 Consts").arg(b.RootElement);
else
rootel = tr("#%1 Direct").arg(b.RootElement);
}
else
{
rootel = tr("#%1 Table[%2]").arg(b.RootElement).arg(b.TableIndex);
}
{
QString name = tr("Constant Buffer %1").arg(ToQStr(b.Buffer));
uint64_t length = b.ByteSize;
uint64_t offset = b.Offset;
int numvars = shaderCBuf ? shaderCBuf->variables.count : 0;
uint32_t bytesize = shaderCBuf ? shaderCBuf->byteSize : 0;
if(b.Immediate && !b.RootValues.empty())
bytesize = uint32_t(b.RootValues.count * 4);
BufferDescription *buf = m_Ctx.GetBuffer(b.Buffer);
if(buf)
name = ToQStr(buf->name);
else
name = tr("Empty");
QString regname = QString::number(reg);
if(shaderCBuf && !shaderCBuf->name.empty())
regname += lit(": ") + ToQStr(shaderCBuf->name);
length = qMin(length, (uint64_t)bytesize);
rows.push_back({rootel, space, regname, name, offset, length, numvars});
}
}
}
m_Common.exportHTMLTable(xml,
{tr("Root Signature Index"), tr("Space"), tr("Register"), tr("Buffer"),
tr("Byte Offset"), tr("Byte Size"), tr("Number of Variables")},
rows);
}
}
void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D12Pipe::Streamout &so)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Stream Out Targets"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D12Pipe::SOBind &o : so.Outputs)
{
QString name = tr("Buffer %1").arg(ToQStr(o.Buffer));
uint64_t length = 0;
QString counterName = tr("Buffer %1").arg(ToQStr(o.WrittenCountBuffer));
uint64_t counterLength = 0;
if(o.Buffer == ResourceId())
{
name = tr("Empty");
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(o.Buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
}
if(o.WrittenCountBuffer == ResourceId())
{
counterName = tr("Empty");
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(o.WrittenCountBuffer);
if(buf)
{
counterName = ToQStr(buf->name);
counterLength = buf->length;
}
}
length = qMin(length, o.Size);
rows.push_back({i, name, o.Offset, length, counterName, o.WrittenCountOffset, counterLength});
i++;
}
m_Common.exportHTMLTable(
xml, {tr("Slot"), tr("Buffer"), tr("Offset"), tr("Byte Length"), tr("Counter Buffer"),
tr("Counter Offset"), tr("Counter Byte Length")},
rows);
}
}
void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D12Pipe::Rasterizer &rs)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("States"));
xml.writeEndElement();
m_Common.exportHTMLTable(xml, {tr("Fill Mode"), tr("Cull Mode"), tr("Front CCW")},
{ToQStr(rs.m_State.fillMode), ToQStr(rs.m_State.cullMode),
rs.m_State.FrontCCW ? tr("Yes") : tr("No")});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Line AA Enable"), tr("Multisample Enable"), tr("Forced Sample Count"),
tr("Conservative Raster"), tr("Sample Mask")},
{rs.m_State.AntialiasedLineEnable ? tr("Yes") : tr("No"),
rs.m_State.MultisampleEnable ? tr("Yes") : tr("No"), rs.m_State.ForcedSampleCount,
rs.m_State.ConservativeRasterization ? tr("Yes") : tr("No"),
Formatter::Format(rs.SampleMask, true)});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Depth Clip"), tr("Depth Bias"), tr("Depth Bias Clamp"), tr("Slope Scaled Bias")},
{rs.m_State.DepthClip ? tr("Yes") : tr("No"), rs.m_State.DepthBias,
Formatter::Format(rs.m_State.DepthBiasClamp),
Formatter::Format(rs.m_State.SlopeScaledDepthBias)});
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Viewports"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D12Pipe::Viewport &v : rs.Viewports)
{
if(v.Width == v.Height && v.Width == 0 && v.Height == 0)
continue;
rows.push_back({i, v.X, v.Y, v.Width, v.Height, v.MinDepth, v.MaxDepth});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("X"), tr("Y"), tr("Width"), tr("Height"),
tr("Min Depth"), tr("Max Depth")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Scissors"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D12Pipe::Scissor &s : rs.Scissors)
{
if(s.right == 0 && s.bottom == 0)
continue;
rows.push_back({i, s.left, s.top, s.right - s.left, s.bottom - s.top});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("X"), tr("Y"), tr("Width"), tr("Height")}, rows);
}
}
void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, D3D12Pipe::OM &om)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Blend State"));
xml.writeEndElement();
QString blendFactor = QFormatStr("%1, %2, %3, %4")
.arg(om.m_BlendState.BlendFactor[0], 0, 'f', 2)
.arg(om.m_BlendState.BlendFactor[1], 0, 'f', 2)
.arg(om.m_BlendState.BlendFactor[2], 0, 'f', 2)
.arg(om.m_BlendState.BlendFactor[3], 0, 'f', 2);
m_Common.exportHTMLTable(xml, {tr("Independent Blend Enable"), tr("Alpha to Coverage"),
tr("Blend Factor"), tr("Multisampling Rate")},
{om.m_BlendState.IndependentBlend ? tr("Yes") : tr("No"),
om.m_BlendState.AlphaToCoverage ? tr("Yes") : tr("No"), blendFactor,
tr("%1x %2 qual").arg(om.multiSampleCount).arg(om.multiSampleQuality)});
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Target Blends"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const D3D12Pipe::Blend &b : om.m_BlendState.Blends)
{
if(i >= om.RenderTargets.count)
continue;
QString mask = QFormatStr("%1%2%3%4")
.arg((b.WriteMask & 0x1) == 0 ? lit("_") : lit("R"))
.arg((b.WriteMask & 0x2) == 0 ? lit("_") : lit("G"))
.arg((b.WriteMask & 0x4) == 0 ? lit("_") : lit("B"))
.arg((b.WriteMask & 0x8) == 0 ? lit("_") : lit("A"));
rows.push_back({i, b.Enabled ? tr("Yes") : tr("No"), b.LogicEnabled ? tr("Yes") : tr("No"),
ToQStr(b.m_Blend.Source), ToQStr(b.m_Blend.Destination),
ToQStr(b.m_Blend.Operation), ToQStr(b.m_AlphaBlend.Source),
ToQStr(b.m_AlphaBlend.Destination), ToQStr(b.m_AlphaBlend.Operation),
ToQStr(b.Logic), mask});
i++;
}
m_Common.exportHTMLTable(
xml,
{
tr("Slot"), tr("Blend Enable"), tr("Logic Enable"), tr("Blend Source"),
tr("Blend Destination"), tr("Blend Operation"), tr("Alpha Blend Source"),
tr("Alpha Blend Destination"), tr("Alpha Blend Operation"), tr("Logic Operation"),
tr("Write Mask"),
},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Depth State"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Depth Test Enable"), tr("Depth Writes Enable"), tr("Depth Function")},
{om.m_State.DepthEnable ? tr("Yes") : tr("No"),
om.m_State.DepthWrites ? tr("Yes") : tr("No"), ToQStr(om.m_State.DepthFunc)});
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Stencil State"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Stencil Test Enable"), tr("Stencil Read Mask"), tr("Stencil Write Mask")},
{om.m_State.StencilEnable ? tr("Yes") : tr("No"),
Formatter::Format(om.m_State.StencilReadMask, true),
Formatter::Format(om.m_State.StencilWriteMask, true)});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Face"), tr("Function"), tr("Pass Operation"), tr("Fail Operation"),
tr("Depth Fail Operation")},
{
{tr("Front"), ToQStr(om.m_State.m_FrontFace.Func), ToQStr(om.m_State.m_FrontFace.PassOp),
ToQStr(om.m_State.m_FrontFace.FailOp), ToQStr(om.m_State.m_FrontFace.DepthFailOp)},
{tr("Back"), ToQStr(om.m_State.m_BackFace.Func), ToQStr(om.m_State.m_BackFace.PassOp),
ToQStr(om.m_State.m_BackFace.FailOp), ToQStr(om.m_State.m_BackFace.DepthFailOp)},
});
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Render targets"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < om.RenderTargets.count; i++)
{
if(om.RenderTargets[i].Resource == ResourceId())
continue;
QVariantList row = exportViewHTML(om.RenderTargets[i], false, NULL, QString());
row.push_front(i);
rows.push_back(row);
}
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Name"), tr("View Type"), tr("Resource Type"),
tr("Width"), tr("Height"), tr("Depth"), tr("Array Size"),
tr("View Format"), tr("Resource Format"), tr("View Parameters"),
},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Depth target"));
xml.writeEndElement();
QString extra;
if(om.DepthReadOnly && om.StencilReadOnly)
extra = tr("Depth & Stencil Read-Only");
else if(om.DepthReadOnly)
extra = tr("Depth Read-Only");
else if(om.StencilReadOnly)
extra = tr("Stencil Read-Only");
m_Common.exportHTMLTable(xml,
{
tr("Name"), tr("View Type"), tr("Resource Type"), tr("Width"),
tr("Height"), tr("Depth"), tr("Array Size"), tr("View Format"),
tr("Resource Format"), tr("View Parameters"),
},
{exportViewHTML(om.DepthTarget, false, NULL, extra)});
}
}
void D3D12PipelineStateViewer::on_exportHTML_clicked()
{
QXmlStreamWriter *xmlptr = m_Common.beginHTMLExport();
if(xmlptr)
{
QXmlStreamWriter &xml = *xmlptr;
const QStringList &stageNames = ui->pipeFlow->stageNames();
const QStringList &stageAbbrevs = ui->pipeFlow->stageAbbreviations();
int stage = 0;
for(const QString &sn : stageNames)
{
xml.writeStartElement(lit("div"));
xml.writeStartElement(lit("a"));
xml.writeAttribute(lit("name"), stageAbbrevs[stage]);
xml.writeEndElement();
xml.writeEndElement();
xml.writeStartElement(lit("div"));
xml.writeAttribute(lit("class"), lit("stage"));
xml.writeStartElement(lit("h1"));
xml.writeCharacters(sn);
xml.writeEndElement();
switch(stage)
{
case 0: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_IA); break;
case 1: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_VS); break;
case 2: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_HS); break;
case 3: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_DS); break;
case 4:
exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_GS);
exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_SO);
break;
case 5: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_RS); break;
case 6: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_PS); break;
case 7: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_OM); break;
case 8: exportHTML(xml, m_Ctx.CurD3D12PipelineState().m_CS); break;
}
xml.writeEndElement();
stage++;
}
m_Common.endHTMLExport(xmlptr);
}
}
void D3D12PipelineStateViewer::on_meshView_clicked()
@@ -32,6 +32,8 @@ namespace Ui
class D3D12PipelineStateViewer;
}
class QXmlStreamWriter;
class RDTreeWidget;
class RDTreeWidgetItem;
struct D3D12ViewTag;
@@ -105,6 +107,14 @@ private:
bool showNode(bool usedSlot, bool filledSlot);
QVariantList exportViewHTML(const D3D12Pipe::View &view, bool rw,
const ShaderResource *shaderInput, const QString &extraParams);
void exportHTML(QXmlStreamWriter &xml, D3D12Pipe::IA &ia);
void exportHTML(QXmlStreamWriter &xml, D3D12Pipe::Shader &sh);
void exportHTML(QXmlStreamWriter &xml, D3D12Pipe::Streamout &so);
void exportHTML(QXmlStreamWriter &xml, D3D12Pipe::Rasterizer &rs);
void exportHTML(QXmlStreamWriter &xml, D3D12Pipe::OM &om);
// keep track of the VB nodes (we want to be able to highlight them easily on hover)
QList<RDTreeWidgetItem *> m_VBNodes;
};
File diff suppressed because it is too large Load Diff
@@ -32,6 +32,8 @@ namespace Ui
class GLPipelineStateViewer;
}
class QXmlStreamWriter;
class RDTreeWidget;
class RDTreeWidgetItem;
class PipelineStateViewer;
@@ -105,6 +107,12 @@ private:
bool showNode(bool usedSlot, bool filledSlot);
void exportHTML(QXmlStreamWriter &xml, GLPipe::VertexInput &vtx);
void exportHTML(QXmlStreamWriter &xml, GLPipe::Shader &sh);
void exportHTML(QXmlStreamWriter &xml, GLPipe::Feedback &xfb);
void exportHTML(QXmlStreamWriter &xml, GLPipe::Rasterizer &rs);
void exportHTML(QXmlStreamWriter &xml, GLPipe::FrameBuffer &fb);
// keep track of the VB nodes (we want to be able to highlight them easily on hover)
QList<RDTreeWidgetItem *> m_VBNodes;
};
@@ -23,6 +23,7 @@
******************************************************************************/
#include "PipelineStateViewer.h"
#include <QXmlStreamWriter>
#include "3rdparty/toolwindowmanager/ToolWindowManager.h"
#include "D3D11PipelineStateViewer.h"
#include "D3D12PipelineStateViewer.h"
@@ -87,20 +88,25 @@ void PipelineStateViewer::OnEventChanged(uint32_t eventID)
m_Current->OnEventChanged(eventID);
}
QString PipelineStateViewer::GetCurrentAPI()
{
if(m_Current == m_D3D11)
return lit("D3D11");
else if(m_Current == m_D3D12)
return lit("D3D12");
else if(m_Current == m_GL)
return lit("OpenGL");
else if(m_Current == m_Vulkan)
return lit("Vulkan");
return lit("");
}
QVariant PipelineStateViewer::persistData()
{
QVariantMap state;
if(m_Current == m_D3D11)
state[lit("type")] = lit("D3D11");
else if(m_Current == m_D3D12)
state[lit("type")] = lit("D3D12");
else if(m_Current == m_GL)
state[lit("type")] = lit("GL");
else if(m_Current == m_Vulkan)
state[lit("type")] = lit("Vulkan");
else
state[lit("type")] = lit("");
state[lit("type")] = GetCurrentAPI();
return state;
}
@@ -186,6 +192,234 @@ void PipelineStateViewer::setToVulkan()
m_Ctx.CurPipelineState().DefaultType = GraphicsAPI::Vulkan;
}
QXmlStreamWriter *PipelineStateViewer::beginHTMLExport()
{
QString filename = RDDialog::getSaveFileName(this, tr("Export pipeline state as HTML"), QString(),
tr("HTML files (*.html)"));
if(!filename.isEmpty())
{
QDir dirinfo = QFileInfo(filename).dir();
if(dirinfo.exists())
{
QFile *f = new QFile(filename, this);
if(f->open(QIODevice::WriteOnly | QIODevice::Truncate))
{
QXmlStreamWriter *xmlptr = new QXmlStreamWriter(f);
QXmlStreamWriter &xml = *xmlptr;
xml.setAutoFormatting(true);
xml.setAutoFormattingIndent(4);
xml.writeStartDocument();
xml.writeDTD(lit("<!DOCTYPE html>"));
xml.writeStartElement(lit("html"));
xml.writeAttribute(lit("lang"), lit("en"));
QString title = tr("%1 EID %2 - %3 Pipeline export")
.arg(QFileInfo(m_Ctx.LogFilename()).fileName())
.arg(m_Ctx.CurEvent())
.arg(GetCurrentAPI());
{
xml.writeStartElement(lit("head"));
xml.writeStartElement(lit("meta"));
xml.writeAttribute(lit("charset"), lit("utf-8"));
xml.writeEndElement();
xml.writeStartElement(lit("meta"));
xml.writeAttribute(lit("http-equiv"), lit("X-UA-Compatible"));
xml.writeAttribute(lit("content"), lit("IE=edge"));
xml.writeEndElement();
xml.writeStartElement(lit("meta"));
xml.writeAttribute(lit("name"), lit("viewport"));
xml.writeAttribute(lit("content"), lit("width=device-width, initial-scale=1"));
xml.writeEndElement();
xml.writeStartElement(lit("meta"));
xml.writeAttribute(lit("name"), lit("description"));
xml.writeAttribute(lit("content"), lit(""));
xml.writeEndElement();
xml.writeStartElement(lit("meta"));
xml.writeAttribute(lit("name"), lit("author"));
xml.writeAttribute(lit("content"), lit(""));
xml.writeEndElement();
xml.writeStartElement(lit("meta"));
xml.writeAttribute(lit("http-equiv"), lit("Content-Type"));
xml.writeAttribute(lit("content"), lit("text/html;charset=utf-8"));
xml.writeEndElement();
xml.writeStartElement(lit("title"));
xml.writeCharacters(title);
xml.writeEndElement();
xml.writeStartElement(lit("style"));
xml.writeComment(lit(R"(
/* If you think this css is ugly/bad, open a pull request! */
body { margin: 20px; }
div.stage { border: 1px solid #BBBBBB; border-radius: 5px; padding: 16px; margin-bottom: 32px; }
div.stage h1 { text-decoration: underline; margin-top: 0px; }
div.stage table { border: 1px solid #AAAAAA; border-collapse: collapse; }
div.stage table thead tr { border-bottom: 1px solid #AAAAAA; background-color: #EEEEFF; }
div.stage table tr th { border-right: 1px solid #AAAAAA; padding: 6px; }
div.stage table tr td { border-right: 1px solid #AAAAAA; background-color: #EEEEEE; padding: 3px; }
)"));
xml.writeEndElement(); // </style>
xml.writeEndElement(); // </head>
}
{
xml.writeStartElement(lit("body"));
xml.writeStartElement(lit("h1"));
xml.writeCharacters(title);
xml.writeEndElement();
xml.writeStartElement(lit("h3"));
{
QString context = tr("Frame %1").arg(m_Ctx.FrameInfo().frameNumber);
const DrawcallDescription *draw = m_Ctx.CurDrawcall();
QList<const DrawcallDescription *> drawstack;
const DrawcallDescription *parent = m_Ctx.GetDrawcall(draw->parent);
while(parent)
{
drawstack.push_front(parent);
parent = m_Ctx.GetDrawcall(parent->parent);
}
for(const DrawcallDescription *d : drawstack)
{
context += QFormatStr(" > %1").arg(ToQStr(d->name));
}
context += QFormatStr(" => %1").arg(ToQStr(draw->name));
xml.writeCharacters(context);
}
xml.writeEndElement(); // </h3>
}
// body is open
return xmlptr;
}
RDDialog::critical(
this, tr("Error exporting pipeline state"),
tr("Couldn't open path %1 for write.\n%2").arg(filename).arg(f->errorString()));
delete f;
return NULL;
}
else
{
RDDialog::critical(this, tr("Invalid directory"),
tr("Cannot find target directory to save to"));
return NULL;
}
}
return NULL;
}
void PipelineStateViewer::exportHTMLTable(QXmlStreamWriter &xml, const QStringList &cols,
const QList<QVariantList> &rows)
{
xml.writeStartElement(lit("table"));
{
xml.writeStartElement(lit("thead"));
xml.writeStartElement(lit("tr"));
for(const QString &col : cols)
{
xml.writeStartElement(lit("th"));
xml.writeCharacters(col);
xml.writeEndElement();
}
xml.writeEndElement();
xml.writeEndElement();
}
{
xml.writeStartElement(lit("tbody"));
if(rows.isEmpty())
{
xml.writeStartElement(lit("tr"));
for(int i = 0; i < cols.count(); i++)
{
xml.writeStartElement(lit("td"));
xml.writeCharacters(lit("-"));
xml.writeEndElement();
}
xml.writeEndElement();
}
else
{
for(const QVariantList &row : rows)
{
xml.writeStartElement(lit("tr"));
for(const QVariant &el : row)
{
xml.writeStartElement(lit("td"));
QMetaType::Type type = (QMetaType::Type)el.type();
if(el.type() == QMetaType::Bool)
xml.writeCharacters(el.toBool() ? tr("True") : tr("False"));
else
xml.writeCharacters(el.toString());
xml.writeEndElement();
}
xml.writeEndElement();
}
}
xml.writeEndElement();
}
xml.writeEndElement();
}
void PipelineStateViewer::exportHTMLTable(QXmlStreamWriter &xml, const QStringList &cols,
const QVariantList &row)
{
exportHTMLTable(xml, cols, QList<QVariantList>({row}));
}
void PipelineStateViewer::endHTMLExport(QXmlStreamWriter *xml)
{
xml->writeEndElement(); // </body>
xml->writeEndElement(); // </html>
xml->writeEndDocument();
// delete the file the writer was writing to
QFile *f = qobject_cast<QFile *>(xml->device());
delete f;
delete xml;
}
bool PipelineStateViewer::PrepareShaderEditing(const ShaderReflection *shaderDetails,
QString &entryFunc, QStringMap &files,
QString &mainfile)
@@ -32,6 +32,8 @@ namespace Ui
class PipelineStateViewer;
}
class QXmlStreamWriter;
class D3D11PipelineStateViewer;
class D3D12PipelineStateViewer;
class GLPipelineStateViewer;
@@ -58,12 +60,18 @@ public:
void OnEventChanged(uint32_t eventID) override;
QVariant persistData();
void setPersistData(const QVariant &persistData);
bool PrepareShaderEditing(const ShaderReflection *shaderDetails, QString &entryFunc,
QStringMap &files, QString &mainfile);
void EditShader(ShaderStage shaderType, ResourceId id, const ShaderReflection *shaderDetails,
const QString &entryFunc, const QStringMap &files, const QString &mainfile);
QXmlStreamWriter *beginHTMLExport();
void exportHTMLTable(QXmlStreamWriter &xml, const QStringList &cols,
const QList<QVariantList> &rows);
void exportHTMLTable(QXmlStreamWriter &xml, const QStringList &cols, const QVariantList &row);
void endHTMLExport(QXmlStreamWriter *xml);
private:
Ui::PipelineStateViewer *ui;
@@ -75,6 +83,8 @@ private:
void setToVulkan();
void reset();
QString GetCurrentAPI();
D3D11PipelineStateViewer *m_D3D11;
D3D12PipelineStateViewer *m_D3D12;
GLPipelineStateViewer *m_GL;
@@ -26,6 +26,7 @@
#include <float.h>
#include <QMouseEvent>
#include <QScrollBar>
#include <QXmlStreamWriter>
#include "3rdparty/toolwindowmanager/ToolWindowManager.h"
#include "Code/Resources.h"
#include "PipelineStateViewer.h"
@@ -609,9 +610,8 @@ void VulkanPipelineStateViewer::clearState()
ui->stencils->clear();
}
RDTreeWidgetItem *VulkanPipelineStateViewer::makeSampler(const QString &bindset,
const QString &slotname,
const VKPipe::BindingElement &descriptor)
QVariantList VulkanPipelineStateViewer::makeSampler(const QString &bindset, const QString &slotname,
const VKPipe::BindingElement &descriptor)
{
QString addressing;
QString addPrefix;
@@ -670,10 +670,13 @@ RDTreeWidgetItem *VulkanPipelineStateViewer::makeSampler(const QString &bindset,
if(descriptor.mipBias != 0.0f)
lod += lit(" Bias %1").arg(descriptor.mipBias);
return new RDTreeWidgetItem(
{QString(), bindset, slotname,
descriptor.immutableSampler ? tr("Immutable Sampler") : tr("Sampler"),
ToQStr(descriptor.name), addressing, filter + lit(", ") + lod});
return {QString(),
bindset,
slotname,
descriptor.immutableSampler ? tr("Immutable Sampler") : tr("Sampler"),
ToQStr(descriptor.name),
addressing,
filter + lit(", ") + lod};
}
void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
@@ -862,7 +865,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
name = ToQStr(buf->name);
restype = TextureDim::Buffer;
if(descriptorLen == 0xFFFFFFFFFFFFFFFFULL)
if(descriptorLen == UINT64_MAX)
descriptorLen = len - descriptorBind->offset;
tag = QVariant::fromValue(
@@ -923,7 +926,8 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
}
else
{
node = makeSampler(QString::number(bindset), slotname, *descriptorBind);
node =
new RDTreeWidgetItem(makeSampler(QString::number(bindset), slotname, *descriptorBind));
if(!filledSlot)
setEmptyRow(node);
@@ -1010,7 +1014,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails,
{
if(!samplers.contains(descriptorBind->sampler))
{
samplerNode = makeSampler(QString(), QString(), *descriptorBind);
samplerNode = new RDTreeWidgetItem(makeSampler(QString(), QString(), *descriptorBind));
if(!filledSlot)
setEmptyRow(samplerNode);
@@ -1167,7 +1171,7 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta
if(buf)
{
name = ToQStr(buf->name);
if(length == 0xFFFFFFFFFFFFFFFFULL)
if(length == UINT64_MAX)
length = buf->length - descriptorBind->offset;
}
@@ -2414,8 +2418,833 @@ void VulkanPipelineStateViewer::shaderSave_clicked()
m_Common.SaveShaderFile(shaderDetails);
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, VKPipe::VertexInput &vi)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Attributes"));
xml.writeEndElement();
QList<QVariantList> rows;
for(const VKPipe::VertexAttribute &attr : vi.attrs)
rows.push_back({attr.location, attr.binding, ToQStr(attr.format.strname), attr.byteoffset});
m_Common.exportHTMLTable(xml, {tr("Location"), tr("Binding"), tr("Format"), tr("Offset")}, rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Bindings"));
xml.writeEndElement();
QList<QVariantList> rows;
for(const VKPipe::VertexBinding &attr : vi.binds)
rows.push_back({attr.vbufferBinding, attr.bytestride,
attr.perInstance ? tr("PER_INSTANCE") : tr("PER_VERTEX")});
m_Common.exportHTMLTable(xml, {tr("Binding"), tr("Byte Stride"), tr("Step Rate")}, rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Vertex Buffers"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const VKPipe::VB &vb : vi.vbuffers)
{
QString name = tr("Buffer %1").arg(ToQStr(vb.buffer));
uint64_t length = 0;
if(vb.buffer == ResourceId())
{
continue;
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(vb.buffer);
if(buf)
{
name = ToQStr(buf->name);
length = buf->length;
}
}
rows.push_back({i, name, vb.offset, length});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Binding"), tr("Buffer"), tr("Offset"), tr("Byte Length")},
rows);
}
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, VKPipe::InputAssembly &ia)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Index Buffer"));
xml.writeEndElement();
BufferDescription *ib = m_Ctx.GetBuffer(ia.ibuffer.buf);
QString name = tr("Empty");
uint64_t length = 0;
if(ib)
{
name = ToQStr(ib->name);
length = ib->length;
}
QString ifmt = lit("UNKNOWN");
if(m_Ctx.CurDrawcall()->indexByteWidth == 2)
ifmt = lit("UINT16");
if(m_Ctx.CurDrawcall()->indexByteWidth == 4)
ifmt = lit("UINT32");
m_Common.exportHTMLTable(
xml, {tr("Buffer"), tr("Format"), tr("Offset"), tr("Byte Length"), tr("Primitive Restart")},
{name, ifmt, ia.ibuffer.offs, length, ia.primitiveRestartEnable ? tr("Yes") : tr("No")});
}
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Primitive Topology"), tr("Tessellation Control Points")},
{ToQStr(m_Ctx.CurDrawcall()->topology), m_Ctx.CurVulkanPipelineState().Tess.numControlPoints});
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, VKPipe::Shader &sh)
{
ShaderReflection *shaderDetails = sh.ShaderDetails;
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Shader"));
xml.writeEndElement();
QString shadername = tr("Unknown");
if(sh.Object == ResourceId())
shadername = tr("Unbound");
else
shadername = ToQStr(sh.name);
if(shaderDetails && shaderDetails->DebugInfo.entryFunc.count > 0)
{
QString entryFunc = ToQStr(shaderDetails->DebugInfo.entryFunc);
if(shaderDetails->DebugInfo.files.count > 0 || entryFunc != lit("main"))
shadername = QFormatStr("%1()").arg(entryFunc);
if(shaderDetails->DebugInfo.files.count > 0)
{
QString shaderfn = QString();
int entryFile = shaderDetails->DebugInfo.entryFile;
if(entryFile < 0 || entryFile >= shaderDetails->DebugInfo.files.count)
entryFile = 0;
shaderfn = QFileInfo(ToQStr(shaderDetails->DebugInfo.files[entryFile].first)).fileName();
shadername = QFormatStr("%1() - %2").arg(entryFunc).arg(shaderfn);
}
}
xml.writeStartElement(lit("p"));
xml.writeCharacters(shadername);
xml.writeEndElement();
if(sh.Object == ResourceId())
return;
}
const VKPipe::Pipeline &pipeline =
(sh.stage == ShaderStage::Compute ? m_Ctx.CurVulkanPipelineState().compute
: m_Ctx.CurVulkanPipelineState().graphics);
if(shaderDetails && shaderDetails->ConstantBlocks.count > 0)
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("UBOs"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < shaderDetails->ConstantBlocks.count; i++)
{
const ConstantBlock &b = shaderDetails->ConstantBlocks[i];
const BindpointMap &bindMap = sh.BindpointMapping.ConstantBlocks[i];
if(!bindMap.used)
continue;
const VKPipe::DescriptorSet &set =
pipeline.DescSets[sh.BindpointMapping.ConstantBlocks[i].bindset];
const VKPipe::DescriptorBinding &bind =
set.bindings[sh.BindpointMapping.ConstantBlocks[i].bind];
QString setname = QString::number(bindMap.bindset);
QString slotname = QFormatStr("%1: %2").arg(bindMap.bind).arg(ToQStr(b.name));
for(uint32_t a = 0; a < bind.descriptorCount; a++)
{
const VKPipe::BindingElement &descriptorBind = bind.binds[a];
ResourceId id = bind.binds[a].res;
if(bindMap.arraySize > 1)
slotname = QFormatStr("%1: %2[%3]").arg(bindMap.bind).arg(ToQStr(b.name)).arg(a);
QString name;
uint64_t byteOffset = descriptorBind.offset;
uint64_t length = descriptorBind.size;
int numvars = b.variables.count;
if(descriptorBind.res == ResourceId())
{
name = tr("Empty");
length = 0;
}
BufferDescription *buf = m_Ctx.GetBuffer(id);
if(buf)
{
name = ToQStr(buf->name);
if(length == UINT64_MAX)
length = buf->length - byteOffset;
}
if(name.isEmpty())
name = tr("UBO %1").arg(ToQStr(descriptorBind.res));
// push constants
if(!b.bufferBacked)
{
setname = QString();
slotname = ToQStr(b.name);
name = tr("Push constants");
byteOffset = 0;
length = 0;
// could maybe get range/size from ShaderVariable.reg if it's filled out
// from SPIR-V side.
}
rows.push_back({setname, slotname, name, byteOffset, length, numvars, b.byteSize});
}
}
m_Common.exportHTMLTable(xml, {tr("Set"), tr("Bind"), tr("Buffer"), tr("Byte Offset"),
tr("Byte Size"), tr("Number of Variables"), tr("Bytes Needed")},
rows);
}
if(shaderDetails->ReadOnlyResources.count > 0)
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Read-only Resources"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < shaderDetails->ReadOnlyResources.count; i++)
{
const ShaderResource &b = shaderDetails->ReadOnlyResources[i];
const BindpointMap &bindMap = sh.BindpointMapping.ReadOnlyResources[i];
if(!bindMap.used)
continue;
const VKPipe::DescriptorSet &set =
pipeline.DescSets[sh.BindpointMapping.ReadOnlyResources[i].bindset];
const VKPipe::DescriptorBinding &bind =
set.bindings[sh.BindpointMapping.ReadOnlyResources[i].bind];
QString setname = QString::number(bindMap.bindset);
QString slotname = QFormatStr("%1: %2").arg(bindMap.bind).arg(ToQStr(b.name));
for(uint32_t a = 0; a < bind.descriptorCount; a++)
{
const VKPipe::BindingElement &descriptorBind = bind.binds[a];
ResourceId id = bind.binds[a].res;
if(bindMap.arraySize > 1)
slotname = QFormatStr("%1: %2[%3]").arg(bindMap.bind).arg(ToQStr(b.name)).arg(a);
QString name;
if(descriptorBind.res == ResourceId())
name = tr("Empty");
BufferDescription *buf = m_Ctx.GetBuffer(id);
if(buf)
name = ToQStr(buf->name);
TextureDescription *tex = m_Ctx.GetTexture(id);
if(tex)
name = ToQStr(tex->name);
if(name.isEmpty())
name = tr("Resource %1").arg(ToQStr(descriptorBind.res));
uint64_t w = 1;
uint32_t h = 1, d = 1;
uint32_t arr = 0;
QString format = tr("Unknown");
QString viewParams;
if(tex)
{
w = tex->width;
h = tex->height;
d = tex->depth;
arr = tex->arraysize;
format = ToQStr(tex->format.strname);
name = ToQStr(tex->name);
if(tex->mips > 1)
{
viewParams = tr("Mips: %1-%2")
.arg(descriptorBind.baseMip)
.arg(descriptorBind.baseMip + descriptorBind.numMip - 1);
}
if(tex->arraysize > 1)
{
if(!viewParams.isEmpty())
viewParams += lit(", ");
viewParams += tr("Layers: %1-%2")
.arg(descriptorBind.baseLayer)
.arg(descriptorBind.baseLayer + descriptorBind.numLayer - 1);
}
}
if(buf)
{
w = buf->length;
h = 0;
d = 0;
a = 0;
format = lit("-");
name = ToQStr(buf->name);
uint64_t length = descriptorBind.size;
if(length == UINT64_MAX)
length = buf->length - descriptorBind.offset;
viewParams =
tr("Byte Range: %1 - %2").arg(descriptorBind.offset).arg(descriptorBind.offset + length);
}
if(bind.type != BindType::Sampler)
rows.push_back(
{setname, slotname, name, ToQStr(bind.type), w, h, d, arr, format, viewParams});
if(bind.type == BindType::ImageSampler || bind.type == BindType::Sampler)
{
name = tr("Sampler %1").arg(ToQStr(descriptorBind.sampler));
if(bind.type == BindType::ImageSampler)
setname = slotname = QString();
QVariantList sampDetails = makeSampler(QString(), QString(), descriptorBind);
rows.push_back({setname, slotname, name, ToQStr(bind.type), QString(), QString(),
QString(), QString(), sampDetails[5], sampDetails[6]});
}
}
}
m_Common.exportHTMLTable(
xml, {tr("Set"), tr("Bind"), tr("Buffer"), tr("Resource Type"), tr("Width"), tr("Height"),
tr("Depth"), tr("Array Size"), tr("Resource Format"), tr("View Parameters")},
rows);
}
if(shaderDetails->ReadWriteResources.count > 0)
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Read-write Resources"));
xml.writeEndElement();
QList<QVariantList> rows;
for(int i = 0; i < shaderDetails->ReadWriteResources.count; i++)
{
const ShaderResource &b = shaderDetails->ReadWriteResources[i];
const BindpointMap &bindMap = sh.BindpointMapping.ReadWriteResources[i];
if(!bindMap.used)
continue;
const VKPipe::DescriptorSet &set =
pipeline.DescSets[sh.BindpointMapping.ReadWriteResources[i].bindset];
const VKPipe::DescriptorBinding &bind =
set.bindings[sh.BindpointMapping.ReadWriteResources[i].bind];
QString setname = bindMap.bindset;
QString slotname = QFormatStr("%1: %2").arg(bindMap.bind).arg(ToQStr(b.name));
for(uint32_t a = 0; a < bind.descriptorCount; a++)
{
const VKPipe::BindingElement &descriptorBind = bind.binds[a];
ResourceId id = bind.binds[a].res;
if(bindMap.arraySize > 1)
slotname = QFormatStr("%1: %2[%3]").arg(bindMap.bind).arg(ToQStr(b.name)).arg(a);
QString name;
if(descriptorBind.res == ResourceId())
name = tr("Empty");
BufferDescription *buf = m_Ctx.GetBuffer(id);
if(buf)
name = ToQStr(buf->name);
TextureDescription *tex = m_Ctx.GetTexture(id);
if(tex)
name = ToQStr(tex->name);
if(name.isEmpty())
name = tr("Resource %1").arg(ToQStr(descriptorBind.res));
uint64_t w = 1;
uint32_t h = 1, d = 1;
uint32_t arr = 0;
QString format = tr("Unknown");
QString viewParams;
if(tex)
{
w = tex->width;
h = tex->height;
d = tex->depth;
arr = tex->arraysize;
format = ToQStr(tex->format.strname);
name = ToQStr(tex->name);
if(tex->mips > 1)
{
viewParams = tr("Mips: %1-%2")
.arg(descriptorBind.baseMip)
.arg(descriptorBind.baseMip + descriptorBind.numMip - 1);
}
if(tex->arraysize > 1)
{
if(!viewParams.isEmpty())
viewParams += lit(", ");
viewParams += tr("Layers: %1-%2")
.arg(descriptorBind.baseLayer)
.arg(descriptorBind.baseLayer + descriptorBind.numLayer - 1);
}
}
if(buf)
{
w = buf->length;
h = 0;
d = 0;
a = 0;
format = lit("-");
name = ToQStr(buf->name);
uint64_t length = descriptorBind.size;
if(length == UINT64_MAX)
length = buf->length - descriptorBind.offset;
viewParams =
tr("Byte Range: %1 - %2").arg(descriptorBind.offset).arg(descriptorBind.offset + length);
}
rows.push_back({setname, slotname, name, ToQStr(bind.type), w, h, d, arr, format, viewParams});
}
}
m_Common.exportHTMLTable(
xml, {tr("Set"), tr("Bind"), tr("Buffer"), tr("Resource Type"), tr("Width"), tr("Height"),
tr("Depth"), tr("Array Size"), tr("Resource Format"), tr("View Parameters")},
rows);
}
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, VKPipe::Raster &rs)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Raster State"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Fill Mode"), tr("Cull Mode"), tr("Front CCW")},
{ToQStr(rs.fillMode), ToQStr(rs.cullMode), rs.FrontCCW ? tr("Yes") : tr("No")});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(xml, {tr("Depth Clip Enable"), tr("Rasterizer Discard Enable")},
{rs.depthClampEnable ? tr("Yes") : tr("No"),
rs.rasterizerDiscardEnable ? tr("Yes") : tr("No")});
xml.writeStartElement(lit("p"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Depth Bias"), tr("Depth Bias Clamp"), tr("Slope Scaled Bias"), tr("Line Width")},
{Formatter::Format(rs.depthBias), Formatter::Format(rs.depthBiasClamp),
Formatter::Format(rs.slopeScaledDepthBias), Formatter::Format(rs.lineWidth)});
}
VKPipe::MultiSample &msaa = m_Ctx.CurVulkanPipelineState().MSAA;
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Multisampling State"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Raster Samples"), tr("Sample-rate shading"), tr("Min Sample Shading Rate"),
tr("Sample Mask")},
{msaa.rasterSamples, msaa.sampleShadingEnable ? tr("Yes") : tr("No"),
Formatter::Format(msaa.minSampleShading), Formatter::Format(msaa.sampleMask, true)});
}
VKPipe::ViewState &vp = m_Ctx.CurVulkanPipelineState().VP;
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Viewports"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const VKPipe::ViewportScissor &vs : vp.viewportScissors)
{
const VKPipe::Viewport &v = vs.vp;
rows.push_back({i, v.x, v.y, v.width, v.height, v.minDepth, v.maxDepth});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("X"), tr("Y"), tr("Width"), tr("Height"),
tr("Min Depth"), tr("Max Depth")},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Scissors"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const VKPipe::ViewportScissor &vs : vp.viewportScissors)
{
const VKPipe::Scissor &s = vs.scissor;
rows.push_back({i, s.x, s.y, s.width, s.height});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("X"), tr("Y"), tr("Width"), tr("Height")}, rows);
}
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, VKPipe::ColorBlend &cb)
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Color Blend State"));
xml.writeEndElement();
QString blendConst = QFormatStr("%1, %2, %3, %4")
.arg(cb.blendConst[0], 0, 'f', 2)
.arg(cb.blendConst[1], 0, 'f', 2)
.arg(cb.blendConst[2], 0, 'f', 2)
.arg(cb.blendConst[3], 0, 'f', 2);
m_Common.exportHTMLTable(
xml, {tr("Alpha to Coverage"), tr("Alpha to One"), tr("Logic Op"), tr("Blend Constant")},
{
cb.alphaToCoverageEnable ? tr("Yes") : tr("No"),
cb.alphaToOneEnable ? tr("Yes") : tr("No"),
cb.logicOpEnable ? ToQStr(cb.logic) : tr("Disabled"), blendConst,
});
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Attachment Blends"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const VKPipe::Blend &b : cb.attachments)
{
rows.push_back(
{i, b.blendEnable ? tr("Yes") : tr("No"), ToQStr(b.blend.Source), ToQStr(b.blend.Destination),
ToQStr(b.blend.Operation), ToQStr(b.alphaBlend.Source), ToQStr(b.alphaBlend.Destination),
ToQStr(b.alphaBlend.Operation), ((b.writeMask & 0x1) == 0 ? lit("_") : lit("R")) +
((b.writeMask & 0x2) == 0 ? lit("_") : lit("G")) +
((b.writeMask & 0x4) == 0 ? lit("_") : lit("B")) +
((b.writeMask & 0x8) == 0 ? lit("_") : lit("A"))});
i++;
}
m_Common.exportHTMLTable(
xml,
{
tr("Slot"), tr("Blend Enable"), tr("Blend Source"), tr("Blend Destination"),
tr("Blend Operation"), tr("Alpha Blend Source"), tr("Alpha Blend Destination"),
tr("Alpha Blend Operation"), tr("Write Mask"),
},
rows);
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, VKPipe::DepthStencil &ds)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Depth State"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Depth Test Enable"), tr("Depth Writes Enable"), tr("Depth Function"),
tr("Depth Bounds")},
{
ds.depthTestEnable ? tr("Yes") : tr("No"), ds.depthWriteEnable ? tr("Yes") : tr("No"),
ToQStr(ds.depthCompareOp), ds.depthBoundsEnable
? QFormatStr("%1 - %2")
.arg(Formatter::Format(ds.minDepthBounds))
.arg(Formatter::Format(ds.maxDepthBounds))
: tr("Disabled"),
});
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Stencil State"));
xml.writeEndElement();
if(ds.stencilTestEnable)
{
QList<QVariantList> rows;
rows.push_back({
tr("Front"), Formatter::Format(ds.front.ref, true),
Formatter::Format(ds.front.compareMask, true),
Formatter::Format(ds.front.writeMask, true), ToQStr(ds.front.Func),
ToQStr(ds.front.PassOp), ToQStr(ds.front.FailOp), ToQStr(ds.front.DepthFailOp),
});
rows.push_back({
tr("back"), Formatter::Format(ds.back.ref, true),
Formatter::Format(ds.back.compareMask, true), Formatter::Format(ds.back.writeMask, true),
ToQStr(ds.back.Func), ToQStr(ds.back.PassOp), ToQStr(ds.back.FailOp),
ToQStr(ds.back.DepthFailOp),
});
m_Common.exportHTMLTable(xml,
{tr("Face"), tr("Ref"), tr("Compare Mask"), tr("Write Mask"),
tr("Function"), tr("Pass Op"), tr("Fail Op"), tr("Depth Fail Op")},
rows);
}
else
{
xml.writeStartElement(lit("p"));
xml.writeCharacters(tr("Disabled"));
xml.writeEndElement();
}
}
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, VKPipe::CurrentPass &pass)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Framebuffer"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("Width"), tr("Height"), tr("Layers")},
{pass.framebuffer.width, pass.framebuffer.height, pass.framebuffer.layers});
QList<QVariantList> rows;
int i = 0;
for(const VKPipe::Attachment &a : pass.framebuffer.attachments)
{
TextureDescription *tex = m_Ctx.GetTexture(a.img);
QString name = tr("Image %1").arg(ToQStr(a.img));
if(tex)
name = ToQStr(tex->name);
rows.push_back({i, name, a.baseMip, a.numMip, a.baseLayer, a.numLayer});
i++;
}
m_Common.exportHTMLTable(xml,
{
tr("Slot"), tr("Image"), tr("First mip"), tr("Number of mips"),
tr("First array layer"), tr("Number of layers"),
},
rows);
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Render Pass"));
xml.writeEndElement();
if(pass.renderpass.inputAttachments.count > 0)
{
QList<QVariantList> inputs;
for(int i = 0; i < pass.renderpass.inputAttachments.count; i++)
inputs.push_back({pass.renderpass.inputAttachments[i]});
m_Common.exportHTMLTable(xml,
{
tr("Input Attachment"),
},
inputs);
xml.writeStartElement(lit("p"));
xml.writeEndElement();
}
if(pass.renderpass.colorAttachments.count > 0)
{
QList<QVariantList> colors;
for(int i = 0; i < pass.renderpass.colorAttachments.count; i++)
colors.push_back({pass.renderpass.colorAttachments[i]});
m_Common.exportHTMLTable(xml,
{
tr("Color Attachment"),
},
colors);
xml.writeStartElement(lit("p"));
xml.writeEndElement();
}
if(pass.renderpass.depthstencilAttachment >= 0)
{
xml.writeStartElement(lit("p"));
xml.writeCharacters(
tr("Depth-stencil Attachment: %1").arg(pass.renderpass.depthstencilAttachment));
xml.writeEndElement();
}
}
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Render Area"));
xml.writeEndElement();
m_Common.exportHTMLTable(
xml, {tr("X"), tr("Y"), tr("Width"), tr("Height")},
{pass.renderArea.x, pass.renderArea.y, pass.renderArea.width, pass.renderArea.height});
}
}
void VulkanPipelineStateViewer::on_exportHTML_clicked()
{
QXmlStreamWriter *xmlptr = m_Common.beginHTMLExport();
if(xmlptr)
{
QXmlStreamWriter &xml = *xmlptr;
const QStringList &stageNames = ui->pipeFlow->stageNames();
const QStringList &stageAbbrevs = ui->pipeFlow->stageAbbreviations();
int stage = 0;
for(const QString &sn : stageNames)
{
xml.writeStartElement(lit("div"));
xml.writeStartElement(lit("a"));
xml.writeAttribute(lit("name"), stageAbbrevs[stage]);
xml.writeEndElement();
xml.writeEndElement();
xml.writeStartElement(lit("div"));
xml.writeAttribute(lit("class"), lit("stage"));
xml.writeStartElement(lit("h1"));
xml.writeCharacters(sn);
xml.writeEndElement();
switch(stage)
{
case 0:
// VTX
xml.writeStartElement(lit("h2"));
xml.writeCharacters(tr("Input Assembly"));
xml.writeEndElement();
exportHTML(xml, m_Ctx.CurVulkanPipelineState().IA);
xml.writeStartElement(lit("h2"));
xml.writeCharacters(tr("Vertex Input"));
xml.writeEndElement();
exportHTML(xml, m_Ctx.CurVulkanPipelineState().VI);
break;
case 1: exportHTML(xml, m_Ctx.CurVulkanPipelineState().m_VS); break;
case 2: exportHTML(xml, m_Ctx.CurVulkanPipelineState().m_TCS); break;
case 3: exportHTML(xml, m_Ctx.CurVulkanPipelineState().m_TES); break;
case 4: exportHTML(xml, m_Ctx.CurVulkanPipelineState().m_GS); break;
case 5: exportHTML(xml, m_Ctx.CurVulkanPipelineState().RS); break;
case 6: exportHTML(xml, m_Ctx.CurVulkanPipelineState().m_FS); break;
case 7:
// FB
xml.writeStartElement(lit("h2"));
xml.writeCharacters(tr("Color Blend"));
xml.writeEndElement();
exportHTML(xml, m_Ctx.CurVulkanPipelineState().CB);
xml.writeStartElement(lit("h2"));
xml.writeCharacters(tr("Depth Stencil"));
xml.writeEndElement();
exportHTML(xml, m_Ctx.CurVulkanPipelineState().DS);
xml.writeStartElement(lit("h2"));
xml.writeCharacters(tr("Current Pass"));
xml.writeEndElement();
exportHTML(xml, m_Ctx.CurVulkanPipelineState().Pass);
break;
case 8: exportHTML(xml, m_Ctx.CurVulkanPipelineState().m_CS); break;
}
xml.writeEndElement();
stage++;
}
m_Common.endHTMLExport(xmlptr);
}
}
void VulkanPipelineStateViewer::on_meshView_clicked()
@@ -32,6 +32,8 @@ namespace Ui
class VulkanPipelineStateViewer;
}
class QXmlStreamWriter;
class RDTreeWidget;
class RDTreeWidgetItem;
class PipelineStateViewer;
@@ -84,8 +86,8 @@ private:
ICaptureContext &m_Ctx;
PipelineStateViewer &m_Common;
RDTreeWidgetItem *makeSampler(const QString &bindset, const QString &slotname,
const VKPipe::BindingElement &descriptor);
QVariantList makeSampler(const QString &bindset, const QString &slotname,
const VKPipe::BindingElement &descriptor);
void addResourceRow(ShaderReflection *shaderDetails, const VKPipe::Shader &stage, int bindset,
int bind, const VKPipe::Pipeline &pipe, RDTreeWidget *resources,
QMap<ResourceId, SamplerData> &samplers);
@@ -116,6 +118,14 @@ private:
bool showNode(bool usedSlot, bool filledSlot);
void exportHTML(QXmlStreamWriter &xml, VKPipe::VertexInput &vi);
void exportHTML(QXmlStreamWriter &xml, VKPipe::InputAssembly &ia);
void exportHTML(QXmlStreamWriter &xml, VKPipe::Shader &sh);
void exportHTML(QXmlStreamWriter &xml, VKPipe::Raster &rs);
void exportHTML(QXmlStreamWriter &xml, VKPipe::ColorBlend &cb);
void exportHTML(QXmlStreamWriter &xml, VKPipe::DepthStencil &ds);
void exportHTML(QXmlStreamWriter &xml, VKPipe::CurrentPass &pass);
// keep track of the VB nodes (we want to be able to highlight them easily on hover)
QList<RDTreeWidgetItem *> m_VBNodes;
QList<RDTreeWidgetItem *> m_BindNodes;