mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-28 17:36:36 +00:00
Add HTML pipeline export for qrenderdoc
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user