Implement VK_EXT_transform_feedback, and use it for mesh output

* For pipelines using tessellation or containing a geometry shader we use
  transform feedback to fetch the output of the vertex pipeline after these
  stages.
This commit is contained in:
baldurk
2018-09-12 11:31:17 +01:00
parent 4dd74c9290
commit b5854f0fec
29 changed files with 1934 additions and 115 deletions
-1
View File
@@ -38,7 +38,6 @@ RenderDoc has support for Vulkan version 1.1, as well as a number of extensions,
* Sparse textures are only supported for non-arrayed 2D textures with no mips.
* Pixel history is not implemented.
* Shader debugging is not currently supported.
* Mesh output of geometry/tessellation shader stages is not available.
Android
-------
+1
View File
@@ -291,6 +291,7 @@ TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, DescriptorSet)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, ImageData)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, ImageLayout)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, SpecializationConstant)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, XFBBuffer)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, VertexBuffer)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, VertexAttribute)
TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, VertexBinding)
+2 -2
View File
@@ -2449,7 +2449,7 @@ void BufferViewer::configureMeshColumns()
uint numComps = sig.format.compCount;
uint elemSize = sig.format.compType == CompType::Double ? 8U : 4U;
if(m_Ctx.CurPipelineState().HasAlignedPostVSData())
if(m_Ctx.CurPipelineState().HasAlignedPostVSData(MeshDataStage::VSOut))
{
if(numComps == 2)
offset = AlignUp(offset, 2U * elemSize);
@@ -2516,7 +2516,7 @@ void BufferViewer::configureMeshColumns()
uint numComps = sig.format.compCount;
uint elemSize = sig.format.compType == CompType::Double ? 8U : 4U;
if(m_Ctx.CurPipelineState().HasAlignedPostVSData())
if(m_Ctx.CurPipelineState().HasAlignedPostVSData(MeshDataStage::GSOut))
{
if(numComps == 2)
offset = AlignUp(offset, 2U * elemSize);
@@ -149,6 +149,9 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
QObject::connect(ui->viBuffers, &RDTreeWidget::leave, this,
&VulkanPipelineStateViewer::vertex_leave);
QObject::connect(ui->xfbBuffers, &RDTreeWidget::itemActivated, this,
&VulkanPipelineStateViewer::resource_itemActivated);
QObject::connect(ui->fbAttach, &RDTreeWidget::itemActivated, this,
&VulkanPipelineStateViewer::resource_itemActivated);
@@ -219,6 +222,21 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
ubo->setInstantTooltips(true);
}
{
RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this);
ui->xfbBuffers->setHeader(header);
ui->xfbBuffers->setColumns({tr("Slot"), tr("Active"), tr("Data Buffer"), tr("Byte Offset"),
tr("Byte Length"), tr("Written Count Buffer"),
tr("Written Count Offset"), tr("Go")});
header->setColumnStretchHints({1, 1, 4, 2, 3, 4, 2, -1});
header->setMinimumSectionSize(40);
ui->xfbBuffers->setHoverIconColumn(7, action, action_hover);
ui->xfbBuffers->setClearSelectionOnFocusLoss(true);
ui->xfbBuffers->setInstantTooltips(true);
}
{
RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this);
ui->viewports->setHeader(header);
@@ -331,6 +349,7 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
ui->csShader->setFont(Formatter::PreferredFont());
ui->csResources->setFont(Formatter::PreferredFont());
ui->csUBOs->setFont(Formatter::PreferredFont());
ui->xfbBuffers->setFont(Formatter::PreferredFont());
ui->viewports->setFont(Formatter::PreferredFont());
ui->scissors->setFont(Formatter::PreferredFont());
ui->renderpass->setFont(Formatter::PreferredFont());
@@ -1818,6 +1837,51 @@ void VulkanPipelineStateViewer::setState()
m_Common.SetupShaderEditButton(b, pipe, stage->resourceId, shaderDetails);
}
bool xfbSet = false;
vs = ui->xfbBuffers->verticalScrollBar()->value();
ui->xfbBuffers->beginUpdate();
ui->xfbBuffers->clear();
for(int i = 0; i < state.transformFeedback.buffers.count(); i++)
{
const VKPipe::XFBBuffer &s = state.transformFeedback.buffers[i];
bool filledSlot = (s.bufferResourceId != ResourceId());
bool usedSlot = (s.active);
if(showNode(usedSlot, filledSlot))
{
qulonglong length = s.byteSize;
BufferDescription *buf = m_Ctx.GetBuffer(s.bufferResourceId);
if(buf && length == UINT64_MAX)
length = buf->length - s.byteOffset;
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{i, s.active ? tr("Active") : tr("Inactive"), s.bufferResourceId, (qulonglong)s.byteOffset,
length, s.counterBufferResourceId, (qulonglong)s.counterBufferOffset, QString()});
node->setTag(QVariant::fromValue(
VulkanBufferTag(false, ~0U, s.bufferResourceId, s.byteOffset, length)));
if(!filledSlot)
setEmptyRow(node);
if(!usedSlot)
setInactiveRow(node);
xfbSet = true;
ui->xfbBuffers->addTopLevelItem(node);
}
}
ui->xfbBuffers->verticalScrollBar()->setValue(vs);
ui->xfbBuffers->clearSelection();
ui->xfbBuffers->endUpdate();
ui->xfbBuffers->setVisible(xfbSet);
ui->xfbGroup->setVisible(xfbSet);
////////////////////////////////////////////////
// Rasterizer
@@ -2145,9 +2209,20 @@ void VulkanPipelineStateViewer::setState()
}
else
{
bool xfbActive = !state.transformFeedback.buffers.isEmpty();
if(state.geometryShader.resourceId == ResourceId() && xfbActive)
{
ui->pipeFlow->setStageName(4, lit("XFB"), tr("Transform Feedback"));
}
else
{
ui->pipeFlow->setStageName(4, lit("GS"), tr("Geometry Shader"));
}
ui->pipeFlow->setStagesEnabled({true, true, state.tessControlShader.resourceId != ResourceId(),
state.tessEvalShader.resourceId != ResourceId(),
state.geometryShader.resourceId != ResourceId(), true,
state.geometryShader.resourceId != ResourceId() || xfbActive, true,
state.fragmentShader.resourceId != ResourceId(), true, false});
}
}
@@ -2229,44 +2304,51 @@ void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, i
{
VulkanBufferTag buf = tag.value<VulkanBufferTag>();
const ShaderResource &shaderRes = buf.rwRes
? stage->reflection->readWriteResources[buf.bindPoint]
: stage->reflection->readOnlyResources[buf.bindPoint];
QString format;
QString format = lit("// struct %1\n").arg(shaderRes.variableType.descriptor.name);
if(shaderRes.variableType.members.count() > 1)
if(stage->reflection &&
buf.bindPoint < (buf.rwRes ? stage->reflection->readWriteResources.size()
: stage->reflection->readOnlyResources.size()))
{
format += lit("// members skipped as they are fixed size:\n");
for(int i = 0; i < shaderRes.variableType.members.count() - 1; i++)
format += QFormatStr("// %1 %2;\n")
.arg(shaderRes.variableType.members[i].type.descriptor.name)
.arg(shaderRes.variableType.members[i].name);
}
const ShaderResource &shaderRes = buf.rwRes
? stage->reflection->readWriteResources[buf.bindPoint]
: stage->reflection->readOnlyResources[buf.bindPoint];
if(!shaderRes.variableType.members.isEmpty())
{
format += lit("{\n") + formatMembers(1, QString(), shaderRes.variableType.members) + lit("}");
}
else
{
const auto &desc = shaderRes.variableType.descriptor;
format = lit("// struct %1\n").arg(shaderRes.variableType.descriptor.name);
format = QString();
if(desc.rowMajorStorage)
format += lit("row_major ");
if(shaderRes.variableType.members.count() > 1)
{
format += lit("// members skipped as they are fixed size:\n");
for(int i = 0; i < shaderRes.variableType.members.count() - 1; i++)
format += QFormatStr("// %1 %2;\n")
.arg(shaderRes.variableType.members[i].type.descriptor.name)
.arg(shaderRes.variableType.members[i].name);
}
format += ToQStr(desc.type);
if(desc.rows > 1 && desc.columns > 1)
format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns);
else if(desc.columns > 1)
format += QString::number(desc.columns);
if(!shaderRes.variableType.members.isEmpty())
{
format += lit("{\n") + formatMembers(1, QString(), shaderRes.variableType.members) + lit("}");
}
else
{
const auto &desc = shaderRes.variableType.descriptor;
if(!desc.name.isEmpty())
format += lit(" ") + desc.name;
format = QString();
if(desc.rowMajorStorage)
format += lit("row_major ");
if(desc.elements > 1)
format += QFormatStr("[%1]").arg(desc.elements);
format += ToQStr(desc.type);
if(desc.rows > 1 && desc.columns > 1)
format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns);
else if(desc.columns > 1)
format += QString::number(desc.columns);
if(!desc.name.isEmpty())
format += lit(" ") + desc.name;
if(desc.elements > 1)
format += QFormatStr("[%1]").arg(desc.elements);
}
}
if(buf.ID != ResourceId())
@@ -2914,6 +2996,50 @@ void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe::
}
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe::TransformFeedback &xfb)
{
{
xml.writeStartElement(lit("h3"));
xml.writeCharacters(tr("Transform Feedback Bindings"));
xml.writeEndElement();
QList<QVariantList> rows;
int i = 0;
for(const VKPipe::XFBBuffer &b : xfb.buffers)
{
QString name = m_Ctx.GetResourceName(b.bufferResourceId);
uint64_t length = b.byteSize;
QString counterName = m_Ctx.GetResourceName(b.counterBufferResourceId);
if(b.bufferResourceId == ResourceId())
{
name = tr("Empty");
}
else
{
BufferDescription *buf = m_Ctx.GetBuffer(b.bufferResourceId);
if(buf && length == UINT64_MAX)
length = buf->length - b.byteOffset;
}
if(b.counterBufferResourceId == ResourceId())
{
counterName = tr("Empty");
}
rows.push_back({i, name, (qulonglong)b.byteOffset, (qulonglong)length, counterName,
(qulonglong)b.counterBufferOffset});
i++;
}
m_Common.exportHTMLTable(xml, {tr("Slot"), tr("Buffer"), tr("Byte Offset"), tr("Byte Length"),
tr("Counter Buffer"), tr("Counter Offset")},
rows);
}
}
void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe::Rasterizer &rs)
{
{
@@ -3248,7 +3374,10 @@ void VulkanPipelineStateViewer::on_exportHTML_clicked()
case 1: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->vertexShader); break;
case 2: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->tessControlShader); break;
case 3: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->tessEvalShader); break;
case 4: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->geometryShader); break;
case 4:
exportHTML(xml, m_Ctx.CurVulkanPipelineState()->geometryShader);
exportHTML(xml, m_Ctx.CurVulkanPipelineState()->transformFeedback);
break;
case 5: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->rasterizer); break;
case 6: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->fragmentShader); break;
case 7:
@@ -117,6 +117,7 @@ private:
void exportHTML(QXmlStreamWriter &xml, const VKPipe::VertexInput &vi);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::InputAssembly &ia);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::Shader &sh);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::TransformFeedback &rs);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::Rasterizer &rs);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::ColorBlendState &cb);
void exportHTML(QXmlStreamWriter &xml, const VKPipe::DepthStencil &ds);
@@ -545,7 +545,7 @@
<property name="icon">
<iconset resource="../../Resources/resources.qrc">
<normaloff>:/page_white_edit.png</normaloff>:/page_white_edit.png</iconset>
</property>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
@@ -1585,6 +1585,61 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="xfbGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Transform Feedback</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_26">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="RDTreeWidget" name="xfbBuffers">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="indentation">
<number>0</number>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
+5 -1
View File
@@ -153,10 +153,14 @@ vectors not crossing ``float4`` boundaries). APIs that use stream-out or transfo
tightly packed data, but APIs that rewrite shaders to dump data might have these alignment
requirements.
:param MeshDataStage stage: The mesh data stage for the output data.
:return: A boolean indicating if post-VS data is aligned.
:rtype: ``bool``
)");
bool HasAlignedPostVSData() const { return IsCaptureLoaded() && IsCaptureVK(); }
bool HasAlignedPostVSData(MeshDataStage stage) const
{
return IsCaptureLoaded() && IsCaptureVK() && stage == MeshDataStage::VSOut;
}
DOCUMENT(R"(For APIs that have explicit barriers, retrieves the current layout of a resource.
:return: The name of the current resource layout.
+66
View File
@@ -483,6 +483,67 @@ struct Tessellation
bool domainOriginUpperLeft = true;
};
DOCUMENT("Describes a single transform feedback binding.");
struct XFBBuffer
{
DOCUMENT("");
XFBBuffer() = default;
XFBBuffer(const XFBBuffer &) = default;
bool operator==(const XFBBuffer &o) const
{
return active == o.active && bufferResourceId == o.bufferResourceId &&
byteOffset == o.byteOffset && byteSize == o.byteSize &&
counterBufferResourceId == o.counterBufferResourceId &&
counterBufferOffset == o.counterBufferOffset;
}
bool operator<(const XFBBuffer &o) const
{
if(!(active == o.active))
return active < o.active;
if(!(bufferResourceId == o.bufferResourceId))
return bufferResourceId < o.bufferResourceId;
if(!(byteOffset == o.byteOffset))
return byteOffset < o.byteOffset;
if(!(byteSize == o.byteSize))
return byteSize < o.byteSize;
if(!(counterBufferResourceId == o.counterBufferResourceId))
return counterBufferResourceId < o.counterBufferResourceId;
if(!(counterBufferOffset == o.counterBufferOffset))
return counterBufferOffset < o.counterBufferOffset;
return false;
}
DOCUMENT("A flag indicating if this buffer is active or not.");
bool active = false;
DOCUMENT("The :class:`ResourceId` of the bound data buffer.");
ResourceId bufferResourceId;
DOCUMENT("The offset in bytes to the start of the data in the :data:`bufferResourceId`.");
uint64_t byteOffset = 0;
DOCUMENT("The size in bytes of the data buffer.");
uint64_t byteSize = 0;
DOCUMENT("The :class:`ResourceId` of the buffer storing the counter value (if set).");
ResourceId counterBufferResourceId;
DOCUMENT("The offset in bytes to the counter in the :data:`counterBufferResourceId`.");
uint64_t counterBufferOffset = 0;
};
DOCUMENT("Describes the state of the fixed-function transform feedback.");
struct TransformFeedback
{
DOCUMENT("");
TransformFeedback() = default;
TransformFeedback(const TransformFeedback &) = default;
DOCUMENT("The bound transform feedback buffers.");
rdcarray<XFBBuffer> buffers;
};
DOCUMENT("Describes a combined viewport and scissor region.");
struct ViewportScissor
{
@@ -864,6 +925,9 @@ struct State
DOCUMENT("A :class:`VKTessellation` describing the tessellation stage.");
Tessellation tessellation;
DOCUMENT("A :class:`VKTransformFeedback` describing the tessellation stage.");
TransformFeedback transformFeedback;
DOCUMENT("A :class:`VKViewState` describing the viewport setup.");
ViewState viewportScissor;
DOCUMENT("A :class:`VKRasterizer` describing rasterization.");
@@ -898,6 +962,8 @@ DECLARE_REFLECTION_STRUCT(VKPipe::VertexInput);
DECLARE_REFLECTION_STRUCT(VKPipe::SpecializationConstant);
DECLARE_REFLECTION_STRUCT(VKPipe::Shader);
DECLARE_REFLECTION_STRUCT(VKPipe::Tessellation);
DECLARE_REFLECTION_STRUCT(VKPipe::XFBBuffer);
DECLARE_REFLECTION_STRUCT(VKPipe::TransformFeedback);
DECLARE_REFLECTION_STRUCT(VKPipe::ViewportScissor);
DECLARE_REFLECTION_STRUCT(VKPipe::ViewState);
DECLARE_REFLECTION_STRUCT(VKPipe::Rasterizer);
@@ -84,6 +84,9 @@ struct SPIRVPatchData
// ID of the base variable
uint32_t ID;
// ID of the struct parent of this variable
uint32_t structID;
// the access chain of indices
std::vector<uint32_t> accessChain;
@@ -95,6 +98,9 @@ struct SPIRVPatchData
// SPIR-V.
std::vector<InterfaceAccess> inputs;
std::vector<InterfaceAccess> outputs;
// the output topology for tessellation and geometry shaders
Topology outTopo = Topology::Unknown;
};
struct SPVModule
@@ -454,6 +454,8 @@ struct SPVTypeData
SPVTypeData *baseType;
uint32_t id;
string name;
bool IsBasicInt() const { return type == eUInt || type == eSInt; }
@@ -3771,10 +3773,10 @@ struct bindpair
typedef bindpair<ConstantBlock> cblockpair;
typedef bindpair<ShaderResource> shaderrespair;
void AddSignatureParameter(bool isInput, ShaderStage stage, uint32_t id, uint32_t &regIndex,
std::vector<uint32_t> accessChain, string varName, SPVTypeData *type,
const vector<SPVDecoration> &decorations, vector<SigParameter> &sigarray,
SPIRVPatchData &patchData)
void AddSignatureParameter(bool isInput, ShaderStage stage, uint32_t id, uint32_t structID,
uint32_t &regIndex, std::vector<uint32_t> accessChain, string varName,
SPVTypeData *type, const vector<SPVDecoration> &decorations,
vector<SigParameter> &sigarray, SPIRVPatchData &patchData)
{
SigParameter sig;
@@ -3783,6 +3785,7 @@ void AddSignatureParameter(bool isInput, ShaderStage stage, uint32_t id, uint32_
SPIRVPatchData::InterfaceAccess patch;
patch.accessChain = accessChain;
patch.ID = id;
patch.structID = structID;
bool rowmajor = true;
@@ -3867,7 +3870,7 @@ void AddSignatureParameter(bool isInput, ShaderStage stage, uint32_t id, uint32_
string baseName = isArray ? StringFormat::Fmt("%s[%u]", varName.c_str(), a) : varName;
AddSignatureParameter(isInput, stage, id, regIndex, patch.accessChain,
AddSignatureParameter(isInput, stage, id, type->id, regIndex, patch.accessChain,
baseName + "." + type->children[c].second, type->children[c].first,
type->childDecorations[c], sigarray, patchData);
@@ -4056,7 +4059,7 @@ void SPVModule::MakeReflection(ShaderStage stage, const string &entryPoint,
nm = StringFormat::Fmt("sig%u", inst->id);
uint32_t dummy = 0;
AddSignatureParameter(isInput, stage, inst->id, dummy, std::vector<uint32_t>(), nm,
AddSignatureParameter(isInput, stage, inst->id, 0, dummy, std::vector<uint32_t>(), nm,
inst->var->type, inst->decorations, *sigarray, patchData);
// eliminate any members of gl_PerVertex that are actually unused and just came along
@@ -4509,7 +4512,27 @@ void SPVModule::MakeReflection(ShaderStage stage, const string &entryPoint,
{
for(const SPVExecutionMode &mode : inst->entry->modes)
{
if(mode.mode == spv::ExecutionModeDepthGreater)
if(mode.mode == spv::ExecutionModeTriangles)
{
patchData.outTopo = Topology::TriangleList;
}
else if(mode.mode == spv::ExecutionModeIsolines)
{
patchData.outTopo = Topology::LineList;
}
else if(mode.mode == spv::ExecutionModeOutputPoints)
{
patchData.outTopo = Topology::PointList;
}
else if(mode.mode == spv::ExecutionModeOutputLineStrip)
{
patchData.outTopo = Topology::LineStrip;
}
else if(mode.mode == spv::ExecutionModeOutputTriangleStrip)
{
patchData.outTopo = Topology::TriangleStrip;
}
else if(mode.mode == spv::ExecutionModeDepthGreater)
{
for(SigParameter &sig : outputs)
{
@@ -4518,7 +4541,7 @@ void SPVModule::MakeReflection(ShaderStage stage, const string &entryPoint,
}
break;
}
if(mode.mode == spv::ExecutionModeDepthLess)
else if(mode.mode == spv::ExecutionModeDepthLess)
{
for(SigParameter &sig : outputs)
{
@@ -5019,6 +5042,7 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
{
op.type = new SPVTypeData();
op.type->type = SPVTypeData::eStruct;
op.type->id = spirv[it + 1];
for(int i = 2; i < WordCount; i++)
{
+8
View File
@@ -469,6 +469,12 @@ enum class VulkanChunk : uint32_t
vkCmdBeginRenderPass2KHR,
vkCmdNextSubpass2KHR,
vkCmdEndRenderPass2KHR,
vkCmdBindTransformFeedbackBuffersEXT,
vkCmdBeginTransformFeedbackEXT,
vkCmdEndTransformFeedbackEXT,
vkCmdBeginQueryIndexedEXT,
vkCmdEndQueryIndexedEXT,
vkCmdDrawIndirectByteCountEXT,
Max,
};
@@ -668,6 +674,7 @@ DECLARE_REFLECTION_STRUCT(VkSubpassEndInfoKHR);
DECLARE_REFLECTION_STRUCT(VkDispatchIndirectCommand);
DECLARE_REFLECTION_STRUCT(VkDrawIndirectCommand);
DECLARE_REFLECTION_STRUCT(VkDrawIndexedIndirectCommand);
DECLARE_REFLECTION_STRUCT(VkPipelineRasterizationStateStreamCreateInfoEXT);
DECLARE_DESERIALISE_TYPE(VkDeviceCreateInfo);
DECLARE_DESERIALISE_TYPE(VkBufferCreateInfo);
@@ -747,6 +754,7 @@ DECLARE_DESERIALISE_TYPE(VkImageFormatListCreateInfoKHR);
DECLARE_DESERIALISE_TYPE(VkRenderPassCreateInfo2KHR);
DECLARE_DESERIALISE_TYPE(VkSubpassBeginInfoKHR);
DECLARE_DESERIALISE_TYPE(VkSubpassEndInfoKHR);
DECLARE_DESERIALISE_TYPE(VkPipelineRasterizationStateStreamCreateInfoEXT);
#if defined(VK_KHR_external_memory_win32) || defined(VK_NV_external_memory_win32)
DECLARE_REFLECTION_STRUCT(VkImportMemoryWin32HandleInfoNV);
+32
View File
@@ -674,6 +674,9 @@ static const VkExtensionProperties supportedExtensions[] = {
{
VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION,
},
{
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, VK_EXT_TRANSFORM_FEEDBACK_SPEC_VERSION,
},
{
VK_EXT_VALIDATION_CACHE_EXTENSION_NAME, VK_EXT_VALIDATION_CACHE_SPEC_VERSION,
},
@@ -2691,6 +2694,21 @@ bool WrappedVulkan::ProcessChunk(ReadSerialiser &ser, VulkanChunk chunk)
case VulkanChunk::vkCmdEndRenderPass2KHR:
return Serialise_vkCmdEndRenderPass2KHR(ser, VK_NULL_HANDLE, NULL);
case VulkanChunk::vkCmdBindTransformFeedbackBuffersEXT:
return Serialise_vkCmdBindTransformFeedbackBuffersEXT(ser, VK_NULL_HANDLE, 0, 0, NULL, NULL,
NULL);
case VulkanChunk::vkCmdBeginTransformFeedbackEXT:
return Serialise_vkCmdBeginTransformFeedbackEXT(ser, VK_NULL_HANDLE, 0, 0, NULL, NULL);
case VulkanChunk::vkCmdEndTransformFeedbackEXT:
return Serialise_vkCmdEndTransformFeedbackEXT(ser, VK_NULL_HANDLE, 0, 0, NULL, NULL);
case VulkanChunk::vkCmdBeginQueryIndexedEXT:
return Serialise_vkCmdBeginQueryIndexedEXT(ser, VK_NULL_HANDLE, VK_NULL_HANDLE, 0, 0, 0);
case VulkanChunk::vkCmdEndQueryIndexedEXT:
return Serialise_vkCmdEndQueryIndexedEXT(ser, VK_NULL_HANDLE, VK_NULL_HANDLE, 0, 0);
case VulkanChunk::vkCmdDrawIndirectByteCountEXT:
return Serialise_vkCmdDrawIndirectByteCountEXT(ser, VK_NULL_HANDLE, 0, 0, VK_NULL_HANDLE, 0,
0, 0);
default:
{
SystemChunk system = (SystemChunk)chunk;
@@ -2912,6 +2930,10 @@ void WrappedVulkan::ReplayLog(uint32_t startEventID, uint32_t endEventID, Replay
{
VkCommandBuffer cmd = m_OutsideCmdBuffer;
// end any active XFB
if(!m_RenderState.xfbcounters.empty())
m_RenderState.EndTransformFeedback(cmd);
// check if the render pass is active - it could have become active
// even if it wasn't before (if the above event was a CmdBeginRenderPass)
if(m_Partial[Primary].renderPassActive)
@@ -3320,6 +3342,16 @@ void WrappedVulkan::AddUsage(VulkanDrawcallTreeNode &drawNode, vector<DebugMessa
drawNode.resourceUsage.push_back(
std::make_pair(state.vbuffers[i], EventUsage(e, ResourceUsage::VertexBuffer)));
for(uint32_t i = state.xfbfirst;
i < state.xfbfirst + state.xfbcount && i < state.xfbbuffers.size(); i++)
{
if(state.xfbbuffers[i] != ResourceId())
{
drawNode.resourceUsage.push_back(
std::make_pair(state.xfbbuffers[i], EventUsage(e, ResourceUsage::StreamOut)));
}
}
//////////////////////////////
// Shaders
+30 -1
View File
@@ -69,6 +69,7 @@ enum class VkIndirectPatchType
DrawIndexedIndirect,
DrawIndirectCount,
DrawIndexedIndirectCount,
DrawIndirectByteCount,
};
struct VkIndirectPatchData
@@ -78,6 +79,7 @@ struct VkIndirectPatchData
VkBuffer buf;
uint32_t count;
uint32_t stride;
uint32_t vertexoffset;
};
struct VulkanDrawcallTreeNode
@@ -531,6 +533,9 @@ private:
uint32_t idxWidth = 0;
ResourceId ibuffer;
std::vector<ResourceId> vbuffers;
std::vector<ResourceId> xfbbuffers;
uint32_t xfbfirst = 0;
uint32_t xfbcount = 0;
ResourceId renderPass;
ResourceId framebuffer;
@@ -711,7 +716,7 @@ private:
{
T *ret = GetTempArray<T>(count);
for(uint32_t i = 0; i < count; i++)
ret[i] = Unwrap(wrapped[i]);
ret[i] = wrapped ? Unwrap(wrapped[i]) : VK_NULL_HANDLE;
return ret;
}
@@ -1926,4 +1931,28 @@ public:
const VkSubpassEndInfoKHR *pSubpassEndInfo);
IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdEndRenderPass2KHR, VkCommandBuffer commandBuffer,
const VkSubpassEndInfoKHR *pSubpassEndInfo);
// VK_EXT_transform_feedback
IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdBindTransformFeedbackBuffersEXT,
VkCommandBuffer commandBuffer, uint32_t firstBinding,
uint32_t bindingCount, const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes);
IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdBeginTransformFeedbackEXT, VkCommandBuffer commandBuffer,
uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets);
IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdEndTransformFeedbackEXT, VkCommandBuffer commandBuffer,
uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets);
IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdBeginQueryIndexedEXT, VkCommandBuffer commandBuffer,
VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags,
uint32_t index);
IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdEndQueryIndexedEXT, VkCommandBuffer commandBuffer,
VkQueryPool queryPool, uint32_t query, uint32_t index);
IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdDrawIndirectByteCountEXT, VkCommandBuffer commandBuffer,
uint32_t instanceCount, uint32_t firstInstance,
VkBuffer counterBuffer, VkDeviceSize counterBufferOffset,
uint32_t counterOffset, uint32_t vertexStride);
};
+7
View File
@@ -1463,6 +1463,7 @@ void VulkanReplay::DestroyResources()
m_VertexPick.Destroy(m_pDriver);
m_PixelPick.Destroy(m_pDriver);
m_Histogram.Destroy(m_pDriver);
m_PostVS.Destroy(m_pDriver);
SAFE_DELETE(m_pAMDCounters);
}
@@ -2412,3 +2413,9 @@ void VulkanReplay::HistogramMinMax::Destroy(WrappedVulkan *driver)
m_HistogramReadback.Destroy();
m_HistogramUBO.Destroy();
}
void VulkanReplay::PostVS::Destroy(WrappedVulkan *driver)
{
if(XFBQueryPool != VK_NULL_HANDLE)
driver->vkDestroyQueryPool(driver->GetDev(), XFBQueryPool, NULL);
}
+24 -1
View File
@@ -352,7 +352,8 @@
CheckExt(KHR_draw_indirect_count, VKXX); \
CheckExt(EXT_validation_cache, VKXX); \
CheckExt(KHR_shared_presentable_image, VKXX); \
CheckExt(KHR_create_renderpass2, VKXX);
CheckExt(KHR_create_renderpass2, VKXX); \
CheckExt(EXT_transform_feedback, VKXX);
#define HookInitVulkanInstanceExts() \
HookInitExtension(KHR_surface, DestroySurfaceKHR); \
@@ -473,6 +474,12 @@
HookInitExtension(KHR_create_renderpass2, CmdBeginRenderPass2KHR); \
HookInitExtension(KHR_create_renderpass2, CmdNextSubpass2KHR); \
HookInitExtension(KHR_create_renderpass2, CmdEndRenderPass2KHR); \
HookInitExtension(EXT_transform_feedback, CmdBindTransformFeedbackBuffersEXT); \
HookInitExtension(EXT_transform_feedback, CmdBeginTransformFeedbackEXT); \
HookInitExtension(EXT_transform_feedback, CmdEndTransformFeedbackEXT); \
HookInitExtension(EXT_transform_feedback, CmdBeginQueryIndexedEXT); \
HookInitExtension(EXT_transform_feedback, CmdEndQueryIndexedEXT); \
HookInitExtension(EXT_transform_feedback, CmdDrawIndirectByteCountEXT); \
HookInitDevice_PlatformSpecific()
#define DefineHooks() \
@@ -1010,6 +1017,22 @@
pSubpassEndInfo); \
HookDefine2(void, vkCmdEndRenderPass2KHR, VkCommandBuffer, commandBuffer, \
const VkSubpassEndInfoKHR *, pSubpassEndInfo); \
HookDefine6(void, vkCmdBindTransformFeedbackBuffersEXT, VkCommandBuffer, commandBuffer, \
uint32_t, firstBinding, uint32_t, bindingCount, const VkBuffer *, pBuffers, \
const VkDeviceSize *, pOffsets, const VkDeviceSize *, pSizes); \
HookDefine5(void, vkCmdBeginTransformFeedbackEXT, VkCommandBuffer, commandBuffer, uint32_t, \
firstBuffer, uint32_t, bufferCount, const VkBuffer *, pCounterBuffers, \
const VkDeviceSize *, pCounterBufferOffsets); \
HookDefine5(void, vkCmdEndTransformFeedbackEXT, VkCommandBuffer, commandBuffer, uint32_t, \
firstBuffer, uint32_t, bufferCount, const VkBuffer *, pCounterBuffers, \
const VkDeviceSize *, pCounterBufferOffsets); \
HookDefine5(void, vkCmdBeginQueryIndexedEXT, VkCommandBuffer, commandBuffer, VkQueryPool, \
queryPool, uint32_t, query, VkQueryControlFlags, flags, uint32_t, index); \
HookDefine4(void, vkCmdEndQueryIndexedEXT, VkCommandBuffer, commandBuffer, VkQueryPool, \
queryPool, uint32_t, query, uint32_t, index); \
HookDefine7(void, vkCmdDrawIndirectByteCountEXT, VkCommandBuffer, commandBuffer, uint32_t, \
instanceCount, uint32_t, firstInstance, VkBuffer, counterBuffer, VkDeviceSize, \
counterBufferOffset, uint32_t, counterOffset, uint32_t, vertexStride); \
HookDefine_PlatformSpecific()
struct VkLayerInstanceDispatchTableExtended : VkLayerInstanceDispatchTable
+10
View File
@@ -281,6 +281,16 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk
depthBiasSlopeFactor = pCreateInfo->pRasterizationState->depthBiasSlopeFactor;
lineWidth = pCreateInfo->pRasterizationState->lineWidth;
// VkPipelineRasterizationStateStreamCreateInfoEXT
rasterizationStream = 0;
const VkPipelineRasterizationStateStreamCreateInfoEXT *rastStream =
(const VkPipelineRasterizationStateStreamCreateInfoEXT *)FindNextStruct(
pCreateInfo->pRasterizationState,
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT);
if(rastStream)
rasterizationStream = rastStream->rasterizationStream;
// VkPipelineRasterizationConservativeStateCreateInfoEXT
conservativeRasterizationMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
extraPrimitiveOverestimationSize = 0.0f;
+3
View File
@@ -190,6 +190,9 @@ struct VulkanCreationInfo
float depthBiasSlopeFactor;
float lineWidth;
// VkPipelineRasterizationStateStreamCreateInfoEXT
uint32_t rasterizationStream;
// VkPipelineRasterizationConservativeStateCreateInfoEXT
VkConservativeRasterizationModeEXT conservativeRasterizationMode;
float extraPrimitiveOverestimationSize;
+743 -58
View File
@@ -31,6 +31,12 @@
#include "vk_debug.h"
#include "vk_shader_cache.h"
struct VkXfbQueryResult
{
uint64_t numPrimitivesWritten;
uint64_t numPrimitivesGenerated;
};
static const char *PatchedMeshOutputEntryPoint = "rdc";
static const uint32_t MeshOutputDispatchWidth = 128;
static const uint32_t MeshOutputTBufferArraySize = 16;
@@ -1070,11 +1076,161 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
editor.StripNops();
}
static void AddXFBAnnotations(const ShaderReflection &refl, const SPIRVPatchData &patchData,
const char *entryName, std::vector<uint32_t> &modSpirv,
uint32_t &xfbStride)
{
SPIRVEditor editor(modSpirv);
rdcarray<SigParameter> outsig = refl.outputSignature;
std::vector<SPIRVPatchData::InterfaceAccess> outpatch = patchData.outputs;
uint32_t entryid = 0;
for(const SPIRVEntry &entry : editor.GetEntries())
{
if(entry.name == entryName)
{
entryid = entry.id;
break;
}
}
bool hasXFB = false;
for(SPIRVIterator it = editor.EndEntries(); it < editor.BeginDebug(); ++it)
{
if(it.opcode() == spv::OpExecutionMode && it.word(1) == entryid &&
it.word(2) == spv::ExecutionModeXfb)
{
hasXFB = true;
break;
}
}
if(hasXFB)
{
for(SPIRVIterator it = editor.BeginDecorations(); it < editor.EndDecorations(); ++it)
{
// remove any existing xfb decorations
if(it.opcode() == spv::OpDecorate &&
(it.word(2) == spv::DecorationXfbBuffer || it.word(2) == spv::DecorationXfbStride))
{
editor.PreModify(it);
SPIRVOperation op(it);
// invalid to have a nop here, but it will be stripped out later
op.nopRemove(1);
op[0] = SPV_NOP;
editor.PostModify(it);
}
// offset is trickier, need to see if it'll match one we want later
if((it.opcode() == spv::OpDecorate && it.word(2) == spv::DecorationOffset) ||
(it.opcode() == spv::OpMemberDecorate && it.word(3) == spv::DecorationOffset))
{
for(size_t i = 0; i < outsig.size(); i++)
{
if(outpatch[i].structID && !outpatch[i].accessChain.empty())
{
if(it.opcode() == spv::OpMemberDecorate && it.word(1) == outpatch[i].structID &&
it.word(2) == outpatch[i].accessChain.back())
{
editor.PreModify(it);
SPIRVOperation op(it);
op.nopRemove(1);
op[0] = SPV_NOP;
editor.PostModify(it);
}
}
else
{
if(it.opcode() == spv::OpDecorate && it.word(1) == outpatch[i].ID)
{
editor.PreModify(it);
SPIRVOperation op(it);
op.nopRemove(1);
op[0] = SPV_NOP;
editor.PostModify(it);
}
}
}
}
}
}
else
{
editor.AddOperation(editor.EndEntries(),
SPIRVOperation(spv::OpExecutionMode, {entryid, spv::ExecutionModeXfb}));
}
editor.AddCapability(spv::CapabilityTransformFeedback);
// find the position output and move it to the front
for(size_t i = 0; i < outsig.size(); i++)
{
if(outsig[i].systemValue == ShaderBuiltin::Position)
{
outsig.insert(0, outsig[i]);
outsig.erase(i + 1);
outpatch.insert(outpatch.begin(), outpatch[i]);
outpatch.erase(outpatch.begin() + i + 1);
break;
}
}
for(size_t i = 0; i < outsig.size(); i++)
{
if(outpatch[i].structID && !outpatch[i].accessChain.empty())
{
editor.AddDecoration(SPIRVOperation(
spv::OpMemberDecorate,
{outpatch[i].structID, outpatch[i].accessChain.back(), spv::DecorationOffset, xfbStride}));
}
else
{
editor.AddDecoration(SPIRVOperation(
spv::OpDecorate, {outpatch[i].ID, (uint32_t)spv::DecorationOffset, xfbStride}));
}
uint32_t compByteSize = 4;
if(outsig[i].compType == CompType::Double)
compByteSize = 8;
xfbStride += outsig[i].compCount * compByteSize;
}
std::set<uint32_t> vars;
for(size_t i = 0; i < outpatch.size(); i++)
{
if(vars.find(outpatch[i].ID) == vars.end())
{
editor.AddDecoration(
SPIRVOperation(spv::OpDecorate, {outpatch[i].ID, (uint32_t)spv::DecorationXfbBuffer, 0}));
editor.AddDecoration(SPIRVOperation(
spv::OpDecorate, {outpatch[i].ID, (uint32_t)spv::DecorationXfbStride, xfbStride}));
vars.insert(outpatch[i].ID);
}
}
editor.StripNops();
}
void VulkanReplay::ClearPostVSCache()
{
VkDevice dev = m_Device;
for(auto it = m_PostVSData.begin(); it != m_PostVSData.end(); ++it)
for(auto it = m_PostVS.Data.begin(); it != m_PostVS.Data.end(); ++it)
{
if(it->second.vsout.idxbuf != VK_NULL_HANDLE)
{
@@ -1085,28 +1241,17 @@ void VulkanReplay::ClearPostVSCache()
m_pDriver->vkFreeMemory(dev, it->second.vsout.bufmem, NULL);
}
m_PostVSData.clear();
m_PostVS.Data.clear();
}
void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
void VulkanReplay::FetchVSOut(uint32_t eventId)
{
// go through any aliasing
if(m_PostVSAlias.find(eventId) != m_PostVSAlias.end())
eventId = m_PostVSAlias[eventId];
if(m_PostVSData.find(eventId) != m_PostVSData.end())
return;
const VulkanRenderState &state = m_pDriver->m_RenderState;
VulkanCreationInfo &creationInfo = m_pDriver->m_CreationInfo;
if(state.graphics.pipeline == ResourceId() || state.renderPass == ResourceId())
return;
const VulkanCreationInfo::Pipeline &pipeInfo = creationInfo.m_Pipeline[state.graphics.pipeline];
if(pipeInfo.shaders[0].module == ResourceId())
return;
const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eventId);
const VulkanCreationInfo::ShaderModule &moduleInfo =
creationInfo.m_ShaderModule[pipeInfo.shaders[0].module];
@@ -1118,29 +1263,24 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
if(refl->outputSignature.empty())
{
// empty vertex output signature
m_PostVSData[eventId].vsin.topo = pipeInfo.topology;
m_PostVSData[eventId].vsout.buf = VK_NULL_HANDLE;
m_PostVSData[eventId].vsout.bufmem = VK_NULL_HANDLE;
m_PostVSData[eventId].vsout.instStride = 0;
m_PostVSData[eventId].vsout.vertStride = 0;
m_PostVSData[eventId].vsout.numViews = 1;
m_PostVSData[eventId].vsout.nearPlane = 0.0f;
m_PostVSData[eventId].vsout.farPlane = 0.0f;
m_PostVSData[eventId].vsout.useIndices = false;
m_PostVSData[eventId].vsout.hasPosOut = false;
m_PostVSData[eventId].vsout.idxbuf = VK_NULL_HANDLE;
m_PostVSData[eventId].vsout.idxbufmem = VK_NULL_HANDLE;
m_PostVS.Data[eventId].vsin.topo = pipeInfo.topology;
m_PostVS.Data[eventId].vsout.buf = VK_NULL_HANDLE;
m_PostVS.Data[eventId].vsout.bufmem = VK_NULL_HANDLE;
m_PostVS.Data[eventId].vsout.instStride = 0;
m_PostVS.Data[eventId].vsout.vertStride = 0;
m_PostVS.Data[eventId].vsout.numViews = 1;
m_PostVS.Data[eventId].vsout.nearPlane = 0.0f;
m_PostVS.Data[eventId].vsout.farPlane = 0.0f;
m_PostVS.Data[eventId].vsout.useIndices = false;
m_PostVS.Data[eventId].vsout.hasPosOut = false;
m_PostVS.Data[eventId].vsout.idxbuf = VK_NULL_HANDLE;
m_PostVS.Data[eventId].vsout.idxbufmem = VK_NULL_HANDLE;
m_PostVSData[eventId].vsout.topo = pipeInfo.topology;
m_PostVS.Data[eventId].vsout.topo = pipeInfo.topology;
return;
}
const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eventId);
if(drawcall == NULL || drawcall->numIndices == 0 || drawcall->numInstances == 0)
return;
// we go through the driver for all these creations since they need to be properly
// registered in order to be put in the partial replay state
VkResult vkr = VK_SUCCESS;
@@ -2108,7 +2248,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// fill destination buffer with 0s to ensure unwritten vertices have sane data
ObjDisp(dev)->CmdFillBuffer(Unwrap(cmd), Unwrap(meshBuffer), 0, bufInfo.size, 0xbaadf00d);
ObjDisp(dev)->CmdFillBuffer(Unwrap(cmd), Unwrap(meshBuffer), 0, bufInfo.size, 0);
VkBufferMemoryBarrier meshbufbarrier = {
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
@@ -2280,36 +2420,36 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
m_pDriver->vkDestroyBufferView(m_Device, uniqIdxBufView, NULL);
}
// fill out m_PostVSData
m_PostVSData[eventId].vsin.topo = pipeCreateInfo.pInputAssemblyState->topology;
m_PostVSData[eventId].vsout.topo = pipeCreateInfo.pInputAssemblyState->topology;
m_PostVSData[eventId].vsout.buf = meshBuffer;
m_PostVSData[eventId].vsout.bufmem = meshMem;
// fill out m_PostVS.Data
m_PostVS.Data[eventId].vsin.topo = pipeCreateInfo.pInputAssemblyState->topology;
m_PostVS.Data[eventId].vsout.topo = pipeCreateInfo.pInputAssemblyState->topology;
m_PostVS.Data[eventId].vsout.buf = meshBuffer;
m_PostVS.Data[eventId].vsout.bufmem = meshMem;
m_PostVSData[eventId].vsout.baseVertex = 0;
m_PostVS.Data[eventId].vsout.baseVertex = 0;
m_PostVSData[eventId].vsout.numViews = numViews;
m_PostVS.Data[eventId].vsout.numViews = numViews;
m_PostVSData[eventId].vsout.vertStride = bufStride;
m_PostVSData[eventId].vsout.nearPlane = nearp;
m_PostVSData[eventId].vsout.farPlane = farp;
m_PostVS.Data[eventId].vsout.vertStride = bufStride;
m_PostVS.Data[eventId].vsout.nearPlane = nearp;
m_PostVS.Data[eventId].vsout.farPlane = farp;
m_PostVSData[eventId].vsout.useIndices = bool(drawcall->flags & DrawFlags::Indexed);
m_PostVSData[eventId].vsout.numVerts = drawcall->numIndices;
m_PostVS.Data[eventId].vsout.useIndices = bool(drawcall->flags & DrawFlags::Indexed);
m_PostVS.Data[eventId].vsout.numVerts = drawcall->numIndices;
m_PostVSData[eventId].vsout.instStride = 0;
m_PostVS.Data[eventId].vsout.instStride = 0;
if(drawcall->flags & DrawFlags::Instanced)
m_PostVSData[eventId].vsout.instStride = uint32_t(bufSize / (drawcall->numInstances * numViews));
m_PostVS.Data[eventId].vsout.instStride = uint32_t(bufSize / (drawcall->numInstances * numViews));
m_PostVSData[eventId].vsout.idxbuf = VK_NULL_HANDLE;
if(m_PostVSData[eventId].vsout.useIndices && state.ibuffer.buf != ResourceId())
m_PostVS.Data[eventId].vsout.idxbuf = VK_NULL_HANDLE;
if(m_PostVS.Data[eventId].vsout.useIndices && state.ibuffer.buf != ResourceId())
{
m_PostVSData[eventId].vsout.idxbuf = rebasedIdxBuf;
m_PostVSData[eventId].vsout.idxbufmem = rebasedIdxBufMem;
m_PostVSData[eventId].vsout.idxFmt = idxsize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32;
m_PostVS.Data[eventId].vsout.idxbuf = rebasedIdxBuf;
m_PostVS.Data[eventId].vsout.idxbufmem = rebasedIdxBufMem;
m_PostVS.Data[eventId].vsout.idxFmt = idxsize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32;
}
m_PostVSData[eventId].vsout.hasPosOut =
m_PostVS.Data[eventId].vsout.hasPosOut =
refl->outputSignature[0].systemValue == ShaderBuiltin::Position;
// delete descriptors. Technically we don't have to free the descriptor sets, but our tracking on
@@ -2330,6 +2470,543 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
// delete shader/shader module
m_pDriver->vkDestroyShaderModule(dev, module, NULL);
}
void VulkanReplay::FetchTessGSOut(uint32_t eventId)
{
VulkanRenderState state = m_pDriver->m_RenderState;
VulkanCreationInfo &creationInfo = m_pDriver->m_CreationInfo;
const VulkanCreationInfo::Pipeline &pipeInfo = creationInfo.m_Pipeline[state.graphics.pipeline];
const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eventId);
// first try geometry stage
int stageIndex = 3;
// if there is no such shader bound, try tessellation
if(!pipeInfo.shaders[stageIndex].refl)
stageIndex = 2;
// if still nothing, do vertex
if(!pipeInfo.shaders[stageIndex].refl)
stageIndex = 0;
ShaderReflection *lastRefl = pipeInfo.shaders[stageIndex].refl;
RDCASSERT(lastRefl);
uint32_t primitiveMultiplier = 1;
// transform feedback expands strips to lists
switch(pipeInfo.shaders[stageIndex].patchData->outTopo)
{
case Topology::PointList:
m_PostVS.Data[eventId].gsout.topo = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
break;
case Topology::LineList:
case Topology::LineStrip:
m_PostVS.Data[eventId].gsout.topo = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
primitiveMultiplier = 2;
break;
default:
RDCERR("Unexpected output topology %s",
ToStr(pipeInfo.shaders[stageIndex].patchData->outTopo).c_str());
// deliberate fallthrough
case Topology::TriangleList:
case Topology::TriangleStrip:
m_PostVS.Data[eventId].gsout.topo = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
primitiveMultiplier = 3;
break;
}
if(lastRefl->outputSignature.empty())
{
// empty vertex output signature
m_PostVS.Data[eventId].gsout.buf = VK_NULL_HANDLE;
m_PostVS.Data[eventId].gsout.bufmem = VK_NULL_HANDLE;
m_PostVS.Data[eventId].gsout.instStride = 0;
m_PostVS.Data[eventId].gsout.vertStride = 0;
m_PostVS.Data[eventId].gsout.numViews = 1;
m_PostVS.Data[eventId].gsout.nearPlane = 0.0f;
m_PostVS.Data[eventId].gsout.farPlane = 0.0f;
m_PostVS.Data[eventId].gsout.useIndices = false;
m_PostVS.Data[eventId].gsout.hasPosOut = false;
m_PostVS.Data[eventId].gsout.idxbuf = VK_NULL_HANDLE;
m_PostVS.Data[eventId].gsout.idxbufmem = VK_NULL_HANDLE;
return;
}
if(!ObjDisp(m_Device)->CmdBeginTransformFeedbackEXT)
{
RDCLOG(
"VK_EXT_transform_feedback_extension not available, can't fetch tessellation/geometry "
"output");
return;
}
const VulkanCreationInfo::ShaderModule &moduleInfo =
creationInfo.m_ShaderModule[pipeInfo.shaders[stageIndex].module];
std::vector<uint32_t> modSpirv = moduleInfo.spirv.spirv;
uint32_t xfbStride = 0;
// adds XFB annotations in order of the output signature (with the position first)
AddXFBAnnotations(*lastRefl, *pipeInfo.shaders[stageIndex].patchData,
pipeInfo.shaders[stageIndex].entryPoint.c_str(), modSpirv, xfbStride);
// create vertex shader with modified code
VkShaderModuleCreateInfo moduleCreateInfo = {
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, NULL, 0,
modSpirv.size() * sizeof(uint32_t), &modSpirv[0],
};
VkResult vkr = VK_SUCCESS;
VkDevice dev = m_Device;
VkShaderModule module;
vkr = m_pDriver->vkCreateShaderModule(dev, &moduleCreateInfo, NULL, &module);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkGraphicsPipelineCreateInfo pipeCreateInfo;
// get pipeline create info
m_pDriver->GetShaderCache()->MakeGraphicsPipelineInfo(pipeCreateInfo, state.graphics.pipeline);
VkPipelineRasterizationStateCreateInfo *rs =
(VkPipelineRasterizationStateCreateInfo *)pipeCreateInfo.pRasterizationState;
rs->rasterizerDiscardEnable = true;
for(uint32_t i = 0; i < pipeCreateInfo.stageCount; i++)
{
VkPipelineShaderStageCreateInfo &stage =
(VkPipelineShaderStageCreateInfo &)pipeCreateInfo.pStages[i];
if(StageIndex(stage.stage) == stageIndex)
{
stage.module = module;
break;
}
}
// create a empty renderpass and framebuffer so we can draw
VkFramebuffer fb = VK_NULL_HANDLE;
VkRenderPass rp = VK_NULL_HANDLE;
VkSubpassDescription sub = {0, VK_PIPELINE_BIND_POINT_GRAPHICS};
VkRenderPassCreateInfo rpinfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 0, NULL, 1, &sub,
};
vkr = m_pDriver->vkCreateRenderPass(m_Device, &rpinfo, NULL, &rp);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, rp, 0, NULL, 16U, 16U, 1,
};
vkr = m_pDriver->vkCreateFramebuffer(m_Device, &fbinfo, NULL, &fb);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
pipeCreateInfo.renderPass = rp;
pipeCreateInfo.subpass = 0;
VkPipeline pipe = VK_NULL_HANDLE;
vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL,
&pipe);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
state.graphics.pipeline = GetResID(pipe);
state.framebuffer = GetResID(fb);
state.renderPass = GetResID(rp);
state.renderArea.offset.x = 0;
state.renderArea.offset.y = 0;
state.renderArea.extent.width = 16;
state.renderArea.extent.height = 16;
// disable any existing XFB
state.xfbbuffers.clear();
state.xfbcounters.clear();
if(m_PostVS.XFBQueryPoolSize < drawcall->numInstances)
{
if(m_PostVS.XFBQueryPoolSize != VK_NULL_HANDLE)
m_pDriver->vkDestroyQueryPool(m_Device, m_PostVS.XFBQueryPool, NULL);
VkQueryPoolCreateInfo info = {
VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
NULL,
0,
VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT,
drawcall->numInstances,
0,
};
vkr = m_pDriver->vkCreateQueryPool(m_Device, &info, NULL, &m_PostVS.XFBQueryPool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_PostVS.XFBQueryPoolSize = drawcall->numInstances;
}
VkBuffer meshBuffer = VK_NULL_HANDLE;
VkDeviceMemory meshMem = VK_NULL_HANDLE;
// start with bare minimum size, which might be enough if no expansion happens
VkDeviceSize bufferSize = 0;
VkDeviceSize dataSize =
uint64_t(drawcall->numIndices) * uint64_t(drawcall->numInstances) * uint64_t(xfbStride);
VkXfbQueryResult queryResult = {};
while(bufferSize < dataSize)
{
bufferSize = dataSize;
if(meshBuffer != VK_NULL_HANDLE)
{
m_pDriver->vkDestroyBuffer(dev, meshBuffer, NULL);
m_pDriver->vkFreeMemory(dev, meshMem, NULL);
meshBuffer = VK_NULL_HANDLE;
meshMem = VK_NULL_HANDLE;
}
VkBufferCreateInfo bufInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
bufInfo.size = bufferSize;
bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufInfo.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
bufInfo.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
vkr = m_pDriver->vkCreateBuffer(dev, &bufInfo, NULL, &meshBuffer);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMemoryRequirements mrq = {0};
m_pDriver->vkGetBufferMemoryRequirements(dev, meshBuffer, &mrq);
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
m_pDriver->GetGPULocalMemoryIndex(mrq.memoryTypeBits),
};
vkr = m_pDriver->vkAllocateMemory(dev, &allocInfo, NULL, &meshMem);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
vkr = m_pDriver->vkBindBufferMemory(dev, meshBuffer, meshMem, 0);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkCommandBuffer cmd = m_pDriver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
vkr = ObjDisp(dev)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
ObjDisp(dev)->CmdResetQueryPool(Unwrap(cmd), Unwrap(m_PostVS.XFBQueryPool), 0, 1);
// fill destination buffer with 0s to ensure unwritten vertices have sane data
ObjDisp(dev)->CmdFillBuffer(Unwrap(cmd), Unwrap(meshBuffer), 0, bufInfo.size, 0xbbaaddee);
VkBufferMemoryBarrier meshbufbarrier = {
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
NULL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(meshBuffer),
0,
bufInfo.size,
};
// wait for the above fill to finish.
DoPipelineBarrier(cmd, 1, &meshbufbarrier);
state.BeginRenderPassAndApplyState(cmd, VulkanRenderState::BindGraphics);
ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), Unwrap(m_PostVS.XFBQueryPool), 0, 0);
ObjDisp(cmd)->CmdBindTransformFeedbackBuffersEXT(Unwrap(cmd), 0, 1, UnwrapPtr(meshBuffer),
&meshbufbarrier.offset, &meshbufbarrier.size);
ObjDisp(cmd)->CmdBeginTransformFeedbackEXT(Unwrap(cmd), 0, 1, NULL, NULL);
if(drawcall->flags & DrawFlags::Indexed)
{
ObjDisp(cmd)->CmdDrawIndexed(Unwrap(cmd), drawcall->numIndices, drawcall->numInstances,
drawcall->indexOffset, drawcall->baseVertex,
drawcall->instanceOffset);
}
else
{
ObjDisp(cmd)->CmdDraw(Unwrap(cmd), drawcall->numIndices, drawcall->numInstances,
drawcall->vertexOffset, drawcall->instanceOffset);
}
ObjDisp(cmd)->CmdEndTransformFeedbackEXT(Unwrap(cmd), 0, 1, NULL, NULL);
ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), Unwrap(m_PostVS.XFBQueryPool), 0);
state.EndRenderPass(cmd);
vkr = ObjDisp(dev)->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_pDriver->SubmitCmds();
m_pDriver->FlushQ();
vkr = ObjDisp(dev)->GetQueryPoolResults(
Unwrap(dev), Unwrap(m_PostVS.XFBQueryPool), 0, 1, sizeof(VkXfbQueryResult), &queryResult,
sizeof(VkXfbQueryResult), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkDeviceSize generatedSize = queryResult.numPrimitivesGenerated * 3 * xfbStride;
// output buffer isn't big enough, delete it and re-run so we recreate it larger
if(generatedSize > dataSize)
dataSize = generatedSize;
}
std::vector<VulkanPostVSData::InstData> instData;
// instanced draws must be replayed one at a time so we can record the number of primitives from
// each drawcall, as due to expansion this can vary per-instance.
if(drawcall->flags & DrawFlags::Instanced && drawcall->numInstances > 1)
{
VkCommandBuffer cmd = m_pDriver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
vkr = ObjDisp(dev)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
ObjDisp(dev)->CmdResetQueryPool(Unwrap(cmd), Unwrap(m_PostVS.XFBQueryPool), 0,
drawcall->numInstances);
state.BeginRenderPassAndApplyState(cmd, VulkanRenderState::BindGraphics);
// do incremental draws to get the output size. We have to do this O(N^2) style because
// there's no way to replay only a single instance. We have to replay 1, 2, 3, ... N
// instances and count the total number of verts each time, then we can see from the
// difference how much each instance wrote.
for(uint32_t inst = 1; inst <= drawcall->numInstances; inst++)
{
ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), Unwrap(m_PostVS.XFBQueryPool), inst - 1, 0);
VkDeviceSize offset = 0;
ObjDisp(cmd)->CmdBindTransformFeedbackBuffersEXT(Unwrap(cmd), 0, 1, UnwrapPtr(meshBuffer),
&offset, &bufferSize);
ObjDisp(cmd)->CmdBeginTransformFeedbackEXT(Unwrap(cmd), 0, 1, NULL, NULL);
if(drawcall->flags & DrawFlags::Indexed)
{
ObjDisp(cmd)->CmdDrawIndexed(Unwrap(cmd), drawcall->numIndices, inst, drawcall->indexOffset,
drawcall->baseVertex, drawcall->instanceOffset);
}
else
{
ObjDisp(cmd)->CmdDraw(Unwrap(cmd), drawcall->numIndices, inst, drawcall->vertexOffset,
drawcall->instanceOffset);
}
ObjDisp(cmd)->CmdEndTransformFeedbackEXT(Unwrap(cmd), 0, 1, NULL, NULL);
ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), Unwrap(m_PostVS.XFBQueryPool), inst - 1);
}
state.EndRenderPass(cmd);
vkr = ObjDisp(dev)->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_pDriver->SubmitCmds();
m_pDriver->FlushQ();
std::vector<VkXfbQueryResult> queryResults;
queryResults.resize(drawcall->numInstances);
vkr = ObjDisp(dev)->GetQueryPoolResults(
Unwrap(dev), Unwrap(m_PostVS.XFBQueryPool), 0, drawcall->numInstances,
sizeof(VkXfbQueryResult) * drawcall->numInstances, queryResults.data(),
sizeof(VkXfbQueryResult), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
uint64_t prevVertCount = 0;
for(uint32_t inst = 0; inst < drawcall->numInstances; inst++)
{
uint64_t vertCount = queryResults[inst].numPrimitivesWritten * primitiveMultiplier;
VulkanPostVSData::InstData d;
d.numVerts = uint32_t(vertCount - prevVertCount);
d.bufOffset = uint32_t(xfbStride * prevVertCount);
prevVertCount = vertCount;
instData.push_back(d);
}
}
float nearp = 0.1f;
float farp = 100.0f;
Vec4f pos0;
bool found = false;
// we read back the buffer in chunks, since we're likely to find a match in the first few
// vertices.
VkDeviceSize readbackoffset = 0;
const VkDeviceSize readbacksize = 1024 * 1024;
while(readbackoffset < bufferSize)
{
bytebuf data;
GetBufferData(GetResID(meshBuffer), readbackoffset, readbacksize, data);
if(data.empty())
break;
if(readbackoffset == 0)
memcpy(&pos0, data.data(), sizeof(pos0));
for(uint32_t i = 0; i < data.size() / xfbStride; i++)
{
//////////////////////////////////////////////////////////////////////////////////
// derive near/far, assuming a standard perspective matrix
//
// the transformation from from pre-projection {Z,W} to post-projection {Z,W}
// is linear. So we can say Zpost = Zpre*m + c . Here we assume Wpre = 1
// and we know Wpost = Zpre from the perspective matrix.
// we can then see from the perspective matrix that
// m = F/(F-N)
// c = -(F*N)/(F-N)
//
// with re-arranging and substitution, we then get:
// N = -c/m
// F = c/(1-m)
//
// so if we can derive m and c then we can determine N and F. We can do this with
// two points, and we pick them reasonably distinct on z to reduce floating-point
// error
Vec4f *pos = (Vec4f *)(data.data() + xfbStride * i);
// skip invalid vertices (w=0)
if(pos->w != 0.0f && fabs(pos->w - pos0.w) > 0.01f && fabs(pos->z - pos0.z) > 0.01f)
{
Vec2f A(pos0.w, pos0.z);
Vec2f B(pos->w, pos->z);
float m = (B.y - A.y) / (B.x - A.x);
float c = B.y - B.x * m;
if(m == 1.0f)
continue;
if(-c / m <= 0.000001f)
continue;
nearp = -c / m;
farp = c / (1 - m);
found = true;
break;
}
}
if(found)
break;
// read the next segment
readbackoffset += readbacksize;
}
// if we didn't find anything, all z's and w's were identical.
// If the z is positive and w greater for the first element then
// we detect this projection as reversed z with infinite far plane
if(!found && pos0.z > 0.0f && pos0.w > pos0.z)
{
nearp = pos0.z;
farp = FLT_MAX;
}
// fill out m_PostVS.Data
m_PostVS.Data[eventId].gsout.buf = meshBuffer;
m_PostVS.Data[eventId].gsout.bufmem = meshMem;
m_PostVS.Data[eventId].gsout.baseVertex = 0;
m_PostVS.Data[eventId].gsout.numViews = 1;
m_PostVS.Data[eventId].gsout.vertStride = xfbStride;
m_PostVS.Data[eventId].gsout.nearPlane = nearp;
m_PostVS.Data[eventId].gsout.farPlane = farp;
m_PostVS.Data[eventId].gsout.useIndices = false;
m_PostVS.Data[eventId].gsout.numVerts =
uint32_t(queryResult.numPrimitivesWritten) * primitiveMultiplier;
// set instance stride to 0. If there's any stride needed, it will be calculated using instData
m_PostVS.Data[eventId].gsout.instStride = 0;
m_PostVS.Data[eventId].gsout.instData = instData;
m_PostVS.Data[eventId].gsout.idxbuf = VK_NULL_HANDLE;
m_PostVS.Data[eventId].gsout.idxbufmem = VK_NULL_HANDLE;
m_PostVS.Data[eventId].gsout.hasPosOut = true;
// delete framebuffer and renderpass
m_pDriver->vkDestroyFramebuffer(dev, fb, NULL);
m_pDriver->vkDestroyRenderPass(dev, rp, NULL);
// delete pipeline
m_pDriver->vkDestroyPipeline(dev, pipe, NULL);
// delete shader/shader module
m_pDriver->vkDestroyShaderModule(dev, module, NULL);
}
void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
{
// go through any aliasing
if(m_PostVS.Alias.find(eventId) != m_PostVS.Alias.end())
eventId = m_PostVS.Alias[eventId];
if(m_PostVS.Data.find(eventId) != m_PostVS.Data.end())
return;
const VulkanRenderState &state = m_pDriver->m_RenderState;
VulkanCreationInfo &creationInfo = m_pDriver->m_CreationInfo;
if(state.graphics.pipeline == ResourceId() || state.renderPass == ResourceId())
return;
const VulkanCreationInfo::Pipeline &pipeInfo = creationInfo.m_Pipeline[state.graphics.pipeline];
if(pipeInfo.shaders[0].module == ResourceId())
return;
const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eventId);
if(drawcall == NULL || drawcall->numIndices == 0 || drawcall->numInstances == 0)
return;
FetchVSOut(eventId);
// if there's no tessellation or geometry shader active, bail out now
if(pipeInfo.shaders[2].module == ResourceId() && pipeInfo.shaders[3].module == ResourceId())
return;
FetchTessGSOut(eventId);
}
struct VulkanInitPostVSCallback : public VulkanDrawcallCallback
{
VulkanInitPostVSCallback(WrappedVulkan *vk, const vector<uint32_t> &events)
@@ -2384,14 +3061,14 @@ MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uin
MeshDataStage stage)
{
// go through any aliasing
if(m_PostVSAlias.find(eventId) != m_PostVSAlias.end())
eventId = m_PostVSAlias[eventId];
if(m_PostVS.Alias.find(eventId) != m_PostVS.Alias.end())
eventId = m_PostVS.Alias[eventId];
VulkanPostVSData postvs;
RDCEraseEl(postvs);
if(m_PostVSData.find(eventId) != m_PostVSData.end())
postvs = m_PostVSData[eventId];
if(m_PostVS.Data.find(eventId) != m_PostVS.Data.end())
postvs = m_PostVS.Data[eventId];
const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eventId);
@@ -2445,5 +3122,13 @@ MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uin
ret.nearPlane = s.nearPlane;
ret.farPlane = s.farPlane;
if(instID < s.instData.size())
{
VulkanPostVSData::InstData inst = s.instData[instID];
ret.vertexByteOffset = inst.bufOffset;
ret.numIndices = inst.numVerts;
}
return ret;
}
+27
View File
@@ -934,6 +934,33 @@ void VulkanReplay::SavePipelineState()
m_VulkanPipelineState.tessellation.domainOriginUpperLeft =
p.tessellationDomainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT;
// Transform feedback
m_VulkanPipelineState.transformFeedback.buffers.resize(state.xfbbuffers.size());
for(size_t i = 0; i < state.xfbbuffers.size(); i++)
{
m_VulkanPipelineState.transformFeedback.buffers[i].bufferResourceId =
rm->GetOriginalID(state.xfbbuffers[i].buf);
m_VulkanPipelineState.transformFeedback.buffers[i].byteOffset = state.xfbbuffers[i].offs;
m_VulkanPipelineState.transformFeedback.buffers[i].byteSize = state.xfbbuffers[i].size;
m_VulkanPipelineState.transformFeedback.buffers[i].active = false;
m_VulkanPipelineState.transformFeedback.buffers[i].counterBufferResourceId = ResourceId();
m_VulkanPipelineState.transformFeedback.buffers[i].counterBufferOffset = 0;
if(i >= state.firstxfbcounter)
{
size_t xfb = i - state.firstxfbcounter;
if(xfb < state.xfbcounters.size())
{
m_VulkanPipelineState.transformFeedback.buffers[i].active = true;
m_VulkanPipelineState.transformFeedback.buffers[i].counterBufferResourceId =
rm->GetOriginalID(state.xfbcounters[xfb].buf);
m_VulkanPipelineState.transformFeedback.buffers[i].counterBufferOffset =
state.xfbcounters[xfb].offs;
}
}
}
// Viewport/Scissors
size_t numViewScissors = p.viewportCount;
m_VulkanPipelineState.viewportScissor.viewportScissors.resize(numViewScissors);
+24 -3
View File
@@ -128,6 +128,12 @@ struct VulkanAMDDrawCallback;
struct VulkanPostVSData
{
struct InstData
{
uint32_t numVerts = 0;
uint32_t bufOffset = 0;
};
struct StageData
{
VkBuffer buf;
@@ -140,6 +146,9 @@ struct VulkanPostVSData
uint32_t vertStride;
uint32_t instStride;
// complex case - expansion per instance
std::vector<InstData> instData;
uint32_t numViews;
bool useIndices;
@@ -249,8 +258,9 @@ public:
void InitPostVSBuffers(uint32_t eventId);
void InitPostVSBuffers(const std::vector<uint32_t> &passEvents);
// indicates that EID alias is the same as eventId
void AliasPostVSBuffers(uint32_t eventId, uint32_t alias) { m_PostVSAlias[alias] = eventId; }
void AliasPostVSBuffers(uint32_t eventId, uint32_t alias) { m_PostVS.Alias[alias] = eventId; }
void ClearPostVSCache();
MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID,
@@ -331,6 +341,9 @@ public:
AMDCounters *GetAMDCounters() { return m_pAMDCounters; }
private:
void FetchVSOut(uint32_t eventId);
void FetchTessGSOut(uint32_t eventId);
bool RenderTextureInternal(TextureDisplay cfg, VkRenderPassBeginInfo rpbegin, int flags);
void CreateTexImageView(VkImageAspectFlags aspectFlags, VkImage liveIm,
@@ -573,8 +586,16 @@ private:
VkPipeline m_MinMaxResultPipe[3] = {VK_NULL_HANDLE};
} m_Histogram;
std::map<uint32_t, VulkanPostVSData> m_PostVSData;
std::map<uint32_t, uint32_t> m_PostVSAlias;
struct PostVS
{
void Destroy(WrappedVulkan *driver);
VkQueryPool XFBQueryPool = VK_NULL_HANDLE;
uint32_t XFBQueryPoolSize = 0;
std::map<uint32_t, VulkanPostVSData> Data;
std::map<uint32_t, uint32_t> Alias;
} m_PostVS;
std::vector<ResourceDescription> m_Resources;
std::map<ResourceId, size_t> m_ResourceIdx;
+22 -1
View File
@@ -262,7 +262,11 @@ SERIALISE_VK_HANDLES();
\
/* VK_EXT_validation_cache */ \
PNEXT_STRUCT(VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT, \
VkShaderModuleValidationCacheCreateInfoEXT)
VkShaderModuleValidationCacheCreateInfoEXT) \
\
/* VK_EXT_transform_feedback */ \
PNEXT_STRUCT(VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT, \
VkPipelineRasterizationStateStreamCreateInfoEXT)
template <typename SerialiserType>
static void SerialiseNext(SerialiserType &ser, VkStructureType &sType, const void *&pNext)
@@ -2560,6 +2564,23 @@ void DoSerialise(SerialiserType &ser, VkShaderModuleValidationCacheCreateInfoEXT
// SERIALISE_MEMBER(validationCache);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VkPipelineRasterizationStateStreamCreateInfoEXT &el)
{
RDCASSERT(ser.IsReading() ||
el.sType == VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT);
SerialiseNext(ser, el.sType, el.pNext);
SERIALISE_MEMBER_TYPED(VkFlagWithNoBits, flags);
SERIALISE_MEMBER(rasterizationStream);
}
template <>
void Deserialise(const VkPipelineRasterizationStateStreamCreateInfoEXT &el)
{
DeserialiseNext(el.pNext);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VkAttachmentDescription2KHR &el)
{
+44
View File
@@ -123,6 +123,50 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd, Pipeli
UnwrapPtr(GetResourceManager()->GetCurrentHandle<VkBuffer>(vbuffers[i].buf)),
&vbuffers[i].offs);
}
for(size_t i = 0; i < xfbbuffers.size(); i++)
{
if(xfbbuffers[i].buf == ResourceId())
continue;
ObjDisp(cmd)->CmdBindTransformFeedbackBuffersEXT(
Unwrap(cmd), (uint32_t)i, 1,
UnwrapPtr(GetResourceManager()->GetCurrentHandle<VkBuffer>(xfbbuffers[i].buf)),
&xfbbuffers[i].offs, &xfbbuffers[i].size);
}
if(!xfbcounters.empty())
{
std::vector<VkBuffer> buffers;
std::vector<VkDeviceSize> offsets;
for(size_t i = 0; i < xfbcounters.size(); i++)
{
buffers.push_back(Unwrap(GetResourceManager()->GetCurrentHandle<VkBuffer>(xfbcounters[i].buf)));
offsets.push_back(xfbcounters[i].offs);
}
ObjDisp(cmd)->CmdBeginTransformFeedbackEXT(
Unwrap(cmd), firstxfbcounter, (uint32_t)xfbcounters.size(), buffers.data(), offsets.data());
}
}
void VulkanRenderState::EndTransformFeedback(VkCommandBuffer cmd)
{
if(!xfbcounters.empty())
{
std::vector<VkBuffer> buffers;
std::vector<VkDeviceSize> offsets;
for(size_t i = 0; i < xfbcounters.size(); i++)
{
buffers.push_back(Unwrap(GetResourceManager()->GetCurrentHandle<VkBuffer>(xfbcounters[i].buf)));
offsets.push_back(xfbcounters[i].offs);
}
ObjDisp(cmd)->CmdEndTransformFeedbackEXT(
Unwrap(cmd), firstxfbcounter, (uint32_t)xfbcounters.size(), buffers.data(), offsets.data());
}
}
void VulkanRenderState::BindPipeline(VkCommandBuffer cmd, PipelineBinding binding, bool subpass0)
+17
View File
@@ -44,6 +44,7 @@ struct VulkanRenderState
VulkanRenderState(WrappedVulkan *driver, VulkanCreationInfo *createInfo);
VulkanRenderState &operator=(const VulkanRenderState &o);
void BeginRenderPassAndApplyState(VkCommandBuffer cmd, PipelineBinding binding);
void EndTransformFeedback(VkCommandBuffer cmd);
void EndRenderPass(VkCommandBuffer cmd);
void BindPipeline(VkCommandBuffer cmd, PipelineBinding binding, bool subpass0);
@@ -103,6 +104,22 @@ struct VulkanRenderState
};
vector<VertBuffer> vbuffers;
struct XFBBuffer
{
ResourceId buf;
VkDeviceSize offs;
VkDeviceSize size;
};
vector<XFBBuffer> xfbbuffers;
struct XFBCounter
{
ResourceId buf;
VkDeviceSize offs;
};
uint32_t firstxfbcounter = 0;
vector<XFBCounter> xfbcounters;
VulkanResourceManager *GetResourceManager();
VulkanCreationInfo *m_CreationInfo;
WrappedVulkan *m_pDriver;
+7 -1
View File
@@ -28,7 +28,7 @@
template <>
std::string DoStringise(const VulkanChunk &el)
{
RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1122, "Chunks changed without updating names");
RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1128, "Chunks changed without updating names");
BEGIN_ENUM_STRINGISE(VulkanChunk)
{
@@ -154,6 +154,12 @@ std::string DoStringise(const VulkanChunk &el)
STRINGISE_ENUM_CLASS(vkCmdBeginRenderPass2KHR);
STRINGISE_ENUM_CLASS(vkCmdNextSubpass2KHR);
STRINGISE_ENUM_CLASS(vkCmdEndRenderPass2KHR);
STRINGISE_ENUM_CLASS(vkCmdBindTransformFeedbackBuffersEXT)
STRINGISE_ENUM_CLASS(vkCmdBeginTransformFeedbackEXT)
STRINGISE_ENUM_CLASS(vkCmdEndTransformFeedbackEXT)
STRINGISE_ENUM_CLASS(vkCmdBeginQueryIndexedEXT)
STRINGISE_ENUM_CLASS(vkCmdEndQueryIndexedEXT)
STRINGISE_ENUM_CLASS(vkCmdDrawIndirectByteCountEXT)
STRINGISE_ENUM_CLASS_NAMED(Max, "Max Chunk");
}
END_ENUM_STRINGISE()
@@ -661,6 +661,7 @@ bool WrappedVulkan::Serialise_vkBeginCommandBuffer(SerialiserType &ser, VkComman
m_Partial[p].partialParent = BakedCommandBuffer;
m_Partial[p].baseEvent = it->baseEvent;
m_Partial[p].renderPassActive = false;
m_RenderState.xfbcounters.clear();
rerecord = true;
partial = true;
@@ -902,6 +903,12 @@ bool WrappedVulkan::Serialise_vkEndCommandBuffer(SerialiserType &ser, VkCommandB
BakedCommandBuffer);
#endif
if(m_Partial[Primary].partialParent == BakedCommandBuffer &&
!m_RenderState.xfbcounters.empty())
{
m_RenderState.EndTransformFeedback(commandBuffer);
}
// finish any render pass that was still active in the primary partial parent
if(m_Partial[Primary].partialParent == BakedCommandBuffer &&
m_Partial[Primary].renderPassActive)
@@ -4276,6 +4283,388 @@ void WrappedVulkan::vkCmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t d
}
}
template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkCmdBindTransformFeedbackBuffersEXT(
SerialiserType &ser, VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount,
const VkBuffer *pBuffers, const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(firstBinding);
SERIALISE_ELEMENT(bindingCount);
SERIALISE_ELEMENT_ARRAY(pBuffers, bindingCount);
SERIALISE_ELEMENT_ARRAY(pOffsets, bindingCount);
SERIALISE_ELEMENT_ARRAY(pSizes, bindingCount);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
{
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
ObjDisp(commandBuffer)
->CmdBindTransformFeedbackBuffersEXT(Unwrap(commandBuffer), firstBinding, bindingCount,
UnwrapArray(pBuffers, bindingCount), pOffsets,
pSizes);
if(ShouldUpdateRenderState(m_LastCmdBufferID))
{
if(m_RenderState.xfbbuffers.size() < firstBinding + bindingCount)
m_RenderState.xfbbuffers.resize(firstBinding + bindingCount);
for(uint32_t i = 0; i < bindingCount; i++)
{
m_RenderState.xfbbuffers[firstBinding + i].buf = GetResID(pBuffers[i]);
m_RenderState.xfbbuffers[firstBinding + i].offs = pOffsets[i];
m_RenderState.xfbbuffers[firstBinding + i].size = pSizes[i];
}
}
}
}
else
{
// track while reading, as we need to track resource usage
if(m_BakedCmdBufferInfo[m_LastCmdBufferID].state.xfbbuffers.size() < firstBinding + bindingCount)
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.xfbbuffers.resize(firstBinding + bindingCount);
for(uint32_t i = 0; i < bindingCount; i++)
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.xfbbuffers[firstBinding + i] =
GetResID(pBuffers[i]);
ObjDisp(commandBuffer)
->CmdBindTransformFeedbackBuffersEXT(Unwrap(commandBuffer), firstBinding, bindingCount,
UnwrapArray(pBuffers, bindingCount), pOffsets, pSizes);
}
}
return true;
}
void WrappedVulkan::vkCmdBindTransformFeedbackBuffersEXT(VkCommandBuffer commandBuffer,
uint32_t firstBinding, uint32_t bindingCount,
const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets,
const VkDeviceSize *pSizes)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
->CmdBindTransformFeedbackBuffersEXT(
Unwrap(commandBuffer), firstBinding, bindingCount,
UnwrapArray(pBuffers, bindingCount), pOffsets, pSizes));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBindTransformFeedbackBuffersEXT);
Serialise_vkCmdBindTransformFeedbackBuffersEXT(ser, commandBuffer, firstBinding, bindingCount,
pBuffers, pOffsets, pSizes);
record->AddChunk(scope.Get());
for(uint32_t i = 0; i < bindingCount; i++)
{
record->MarkResourceFrameReferenced(GetResID(pBuffers[i]), eFrameRef_Read);
record->MarkResourceFrameReferenced(GetRecord(pBuffers[i])->baseResource, eFrameRef_Read);
if(GetRecord(pBuffers[i])->sparseInfo)
record->cmdInfo->sparse.insert(GetRecord(pBuffers[i])->sparseInfo);
}
}
}
template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkCmdBeginTransformFeedbackEXT(
SerialiserType &ser, VkCommandBuffer commandBuffer, uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers, const VkDeviceSize *pCounterBufferOffsets)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(firstBuffer);
SERIALISE_ELEMENT(bufferCount);
SERIALISE_ELEMENT_ARRAY(pCounterBuffers, bufferCount);
SERIALISE_ELEMENT_ARRAY(pCounterBufferOffsets, bufferCount);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
{
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
// only if we're partially recording do we update this state
if(ShouldUpdateRenderState(m_LastCmdBufferID))
{
m_RenderState.firstxfbcounter = firstBuffer;
m_RenderState.xfbcounters.resize(bufferCount);
for(uint32_t i = 0; i < bufferCount; i++)
{
m_RenderState.xfbcounters[i].buf =
pCounterBuffers ? GetResID(pCounterBuffers[i]) : ResourceId();
m_RenderState.xfbcounters[i].offs = pCounterBufferOffsets ? pCounterBufferOffsets[i] : 0;
}
}
ObjDisp(commandBuffer)
->CmdBeginTransformFeedbackEXT(Unwrap(commandBuffer), firstBuffer, bufferCount,
UnwrapArray(pCounterBuffers, bufferCount),
pCounterBufferOffsets);
}
}
else
{
ObjDisp(commandBuffer)
->CmdBeginTransformFeedbackEXT(Unwrap(commandBuffer), firstBuffer, bufferCount,
UnwrapArray(pCounterBuffers, bufferCount),
pCounterBufferOffsets);
// track while reading, for fetching the right set of outputs in AddDrawcall
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.xfbfirst = firstBuffer;
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.xfbcount = bufferCount;
}
}
return true;
}
void WrappedVulkan::vkCmdBeginTransformFeedbackEXT(VkCommandBuffer commandBuffer,
uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
->CmdBeginTransformFeedbackEXT(
Unwrap(commandBuffer), firstBuffer, bufferCount,
UnwrapArray(pCounterBuffers, bufferCount), pCounterBufferOffsets));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
ser.SetDrawChunk();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBeginTransformFeedbackEXT);
Serialise_vkCmdBeginTransformFeedbackEXT(ser, commandBuffer, firstBuffer, bufferCount,
pCounterBuffers, pCounterBufferOffsets);
record->AddChunk(scope.Get());
}
}
template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkCmdEndTransformFeedbackEXT(
SerialiserType &ser, VkCommandBuffer commandBuffer, uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers, const VkDeviceSize *pCounterBufferOffsets)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(firstBuffer);
SERIALISE_ELEMENT(bufferCount);
SERIALISE_ELEMENT_ARRAY(pCounterBuffers, bufferCount);
SERIALISE_ELEMENT_ARRAY(pCounterBufferOffsets, bufferCount);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
{
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
// only if we're partially recording do we update this state
if(ShouldUpdateRenderState(m_LastCmdBufferID))
{
m_RenderState.firstxfbcounter = 0;
m_RenderState.xfbcounters.clear();
}
ObjDisp(commandBuffer)
->CmdEndTransformFeedbackEXT(Unwrap(commandBuffer), firstBuffer, bufferCount,
UnwrapArray(pCounterBuffers, bufferCount),
pCounterBufferOffsets);
}
}
else
{
ObjDisp(commandBuffer)
->CmdEndTransformFeedbackEXT(Unwrap(commandBuffer), firstBuffer, bufferCount,
UnwrapArray(pCounterBuffers, bufferCount),
pCounterBufferOffsets);
// track while reading, for fetching the right set of outputs in AddDrawcall
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.xfbfirst = 0;
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.xfbcount = 0;
}
}
return true;
}
void WrappedVulkan::vkCmdEndTransformFeedbackEXT(VkCommandBuffer commandBuffer,
uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
->CmdEndTransformFeedbackEXT(
Unwrap(commandBuffer), firstBuffer, bufferCount,
UnwrapArray(pCounterBuffers, bufferCount), pCounterBufferOffsets));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
ser.SetDrawChunk();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdEndTransformFeedbackEXT);
Serialise_vkCmdEndTransformFeedbackEXT(ser, commandBuffer, firstBuffer, bufferCount,
pCounterBuffers, pCounterBufferOffsets);
record->AddChunk(scope.Get());
}
}
template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkCmdBeginQueryIndexedEXT(SerialiserType &ser,
VkCommandBuffer commandBuffer,
VkQueryPool queryPool, uint32_t query,
VkQueryControlFlags flags, uint32_t index)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(queryPool);
SERIALISE_ELEMENT(query);
SERIALISE_ELEMENT_TYPED(VkQueryControlFlagBits, flags);
SERIALISE_ELEMENT(index);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
else
commandBuffer = VK_NULL_HANDLE;
}
if(commandBuffer != VK_NULL_HANDLE)
ObjDisp(commandBuffer)
->CmdBeginQueryIndexedEXT(Unwrap(commandBuffer), Unwrap(queryPool), query, flags, index);
}
return true;
}
void WrappedVulkan::vkCmdBeginQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t query, VkQueryControlFlags flags,
uint32_t index)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(
ObjDisp(commandBuffer)
->CmdBeginQueryIndexedEXT(Unwrap(commandBuffer), Unwrap(queryPool), query, flags, index));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBeginQueryIndexedEXT);
Serialise_vkCmdBeginQueryIndexedEXT(ser, commandBuffer, queryPool, query, flags, index);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(queryPool), eFrameRef_Read);
}
}
template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkCmdEndQueryIndexedEXT(SerialiserType &ser,
VkCommandBuffer commandBuffer,
VkQueryPool queryPool, uint32_t query,
uint32_t index)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(queryPool);
SERIALISE_ELEMENT(query);
SERIALISE_ELEMENT(index);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID))
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
else
commandBuffer = VK_NULL_HANDLE;
}
if(commandBuffer != VK_NULL_HANDLE)
ObjDisp(commandBuffer)
->CmdEndQueryIndexedEXT(Unwrap(commandBuffer), Unwrap(queryPool), query, index);
}
return true;
}
void WrappedVulkan::vkCmdEndQueryIndexedEXT(VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t query, uint32_t index)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(
ObjDisp(commandBuffer)
->CmdEndQueryIndexedEXT(Unwrap(commandBuffer), Unwrap(queryPool), query, index));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdEndQueryIndexedEXT);
Serialise_vkCmdEndQueryIndexedEXT(ser, commandBuffer, queryPool, query, index);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(queryPool), eFrameRef_Read);
}
}
INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkCreateCommandPool, VkDevice device,
const VkCommandPoolCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool);
@@ -4396,4 +4785,22 @@ INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdInsertDebugUtilsLabelEXT, VkCommandBu
const VkDebugUtilsLabelEXT *pLabelInfo);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdSetDeviceMask, VkCommandBuffer commandBuffer,
uint32_t deviceMask);
uint32_t deviceMask);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdBindTransformFeedbackBuffersEXT,
VkCommandBuffer commandBuffer, uint32_t firstBinding,
uint32_t bindingCount, const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdBeginTransformFeedbackEXT, VkCommandBuffer commandBuffer,
uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdEndTransformFeedbackEXT, VkCommandBuffer commandBuffer,
uint32_t firstBuffer, uint32_t bufferCount,
const VkBuffer *pCounterBuffers,
const VkDeviceSize *pCounterBufferOffsets);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdBeginQueryIndexedEXT, VkCommandBuffer commandBuffer,
VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags,
uint32_t index);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdEndQueryIndexedEXT, VkCommandBuffer commandBuffer,
VkQueryPool queryPool, uint32_t query, uint32_t index);
@@ -1080,6 +1080,20 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi
RDCLOG("Enabling VK_MVK_moltenvk");
}
// enable VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME if it's available, to fetch mesh output in
// tessellation/geometry stages
if(supportedExtensions.find(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME) != supportedExtensions.end())
{
Extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
RDCLOG("Enabling VK_EXT_transform_feedback_extension");
}
else
{
RDCWARN(
"VK_EXT_transform_feedback_extension not available, mesh output from "
"geometry/tessellation stages will not be available");
}
createInfo.enabledLayerCount = (uint32_t)Layers.size();
const char **layerArray = NULL;
@@ -52,6 +52,7 @@ VkIndirectPatchData WrappedVulkan::FetchIndirectData(VkIndirectPatchType type,
case VkIndirectPatchType::DrawIndexedIndirectCount:
dataSize = sizeof(VkDrawIndexedIndirectCommand) + (count > 0 ? count - 1 : 0) * stride;
break;
case VkIndirectPatchType::DrawIndirectByteCount: dataSize = 4; break;
}
bufInfo.size = AlignUp16(dataSize);
@@ -80,6 +81,9 @@ VkIndirectPatchData WrappedVulkan::FetchIndirectData(VkIndirectPatchType type,
dataSize,
};
if(type == VkIndirectPatchType::DrawIndirectByteCount)
buf.srcAccessMask |= VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;
ObjDisp(commandBuffer)
->CmdPipelineBarrier(Unwrap(commandBuffer), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, NULL, 1, &buf, 0, NULL);
@@ -3069,6 +3073,134 @@ void WrappedVulkan::vkCmdDrawIndexedIndirectCountKHR(VkCommandBuffer commandBuff
}
}
template <typename SerialiserType>
bool WrappedVulkan::Serialise_vkCmdDrawIndirectByteCountEXT(
SerialiserType &ser, VkCommandBuffer commandBuffer, uint32_t instanceCount,
uint32_t firstInstance, VkBuffer counterBuffer, VkDeviceSize counterBufferOffset,
uint32_t counterOffset, uint32_t vertexStride)
{
SERIALISE_ELEMENT(commandBuffer);
SERIALISE_ELEMENT(instanceCount);
SERIALISE_ELEMENT(firstInstance);
SERIALISE_ELEMENT(counterBuffer);
SERIALISE_ELEMENT(counterBufferOffset);
SERIALISE_ELEMENT(counterOffset);
SERIALISE_ELEMENT(vertexStride);
Serialise_DebugMessages(ser);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
// do execution (possibly partial)
if(IsActiveReplaying(m_State))
{
if(InRerecordRange(m_LastCmdBufferID) && IsDrawInRenderPass())
{
commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
uint32_t eventId = HandlePreCallback(commandBuffer);
ObjDisp(commandBuffer)
->CmdDrawIndirectByteCountEXT(Unwrap(commandBuffer), instanceCount, firstInstance,
Unwrap(counterBuffer), counterBufferOffset, counterOffset,
vertexStride);
if(eventId && m_DrawcallCallback->PostDraw(eventId, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndirectByteCountEXT(Unwrap(commandBuffer), instanceCount, firstInstance,
Unwrap(counterBuffer), counterBufferOffset,
counterOffset, vertexStride);
m_DrawcallCallback->PostRedraw(eventId, commandBuffer);
}
}
}
else
{
VkIndirectPatchData indirectPatch =
FetchIndirectData(VkIndirectPatchType::DrawIndirectByteCount, commandBuffer,
counterBuffer, counterBufferOffset, 1, vertexStride);
indirectPatch.vertexoffset = counterOffset;
ObjDisp(commandBuffer)
->CmdDrawIndirectByteCountEXT(Unwrap(commandBuffer), instanceCount, firstInstance,
Unwrap(counterBuffer), counterBufferOffset, counterOffset,
vertexStride);
string name = "vkCmdDrawIndirectByteCountEXT";
if(!IsDrawInRenderPass())
{
AddDebugMessage(MessageCategory::Execution, MessageSeverity::High,
MessageSource::IncorrectAPIUse,
"Drawcall in happening outside of render pass, or in secondary command "
"buffer without RENDER_PASS_CONTINUE_BIT");
}
DrawcallDescription draw;
AddEvent();
draw.name = name;
draw.instanceOffset = firstInstance;
draw.numInstances = instanceCount;
draw.flags = DrawFlags::Drawcall | DrawFlags::Instanced | DrawFlags::Indirect;
AddDrawcall(draw, true);
VulkanDrawcallTreeNode &drawNode = GetDrawcallStack().back()->children.back();
drawNode.indirectPatch = indirectPatch;
drawNode.resourceUsage.push_back(std::make_pair(
GetResID(counterBuffer), EventUsage(drawNode.draw.eventId, ResourceUsage::Indirect)));
return true;
}
}
return true;
}
void WrappedVulkan::vkCmdDrawIndirectByteCountEXT(VkCommandBuffer commandBuffer,
uint32_t instanceCount, uint32_t firstInstance,
VkBuffer counterBuffer,
VkDeviceSize counterBufferOffset,
uint32_t counterOffset, uint32_t vertexStride)
{
SCOPED_DBG_SINK();
SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
->CmdDrawIndirectByteCountEXT(Unwrap(commandBuffer), instanceCount,
firstInstance, Unwrap(counterBuffer),
counterBufferOffset, counterOffset,
vertexStride));
if(IsCaptureMode(m_State))
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
ser.SetDrawChunk();
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdDrawIndirectByteCountEXT);
Serialise_vkCmdDrawIndirectByteCountEXT(ser, commandBuffer, instanceCount, firstInstance,
counterBuffer, counterBufferOffset, counterOffset,
vertexStride);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(counterBuffer), eFrameRef_Read);
record->MarkResourceFrameReferenced(GetRecord(counterBuffer)->baseResource, eFrameRef_Read);
if(GetRecord(counterBuffer)->sparseInfo)
record->cmdInfo->sparse.insert(GetRecord(counterBuffer)->sparseInfo);
}
}
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDraw, VkCommandBuffer commandBuffer, uint32_t vertexCount,
uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance);
@@ -3143,4 +3275,9 @@ INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDrawIndirectCountKHR, VkCommandBuffer
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDrawIndexedIndirectCountKHR,
VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
VkBuffer countBuffer, VkDeviceSize countBufferOffset,
uint32_t maxDrawCount, uint32_t stride);
uint32_t maxDrawCount, uint32_t stride);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDrawIndirectByteCountEXT, VkCommandBuffer commandBuffer,
uint32_t instanceCount, uint32_t firstInstance,
VkBuffer counterBuffer, VkDeviceSize counterBufferOffset,
uint32_t counterOffset, uint32_t vertexStride);
@@ -449,6 +449,17 @@ bool WrappedVulkan::PatchIndirectDraw(VkIndirectPatchType type, DrawcallDescript
valid = true;
}
}
else if(type == VkIndirectPatchType::DrawIndirectByteCount)
{
if(argptr && argptr + 4 <= argend)
{
uint32_t *arg = (uint32_t *)argptr;
draw.numIndices = *arg;
valid = true;
}
}
else if(type == VkIndirectPatchType::DrawIndexedIndirect ||
type == VkIndirectPatchType::DrawIndexedIndirectCount)
{
@@ -540,7 +551,8 @@ void WrappedVulkan::InsertDrawsAndRefreshIDs(vector<VulkanDrawcallTreeNode> &cmd
n.draw.dispatchDimension[1] = args->y;
n.draw.dispatchDimension[2] = args->z;
}
else if(n.indirectPatch.type == VkIndirectPatchType::DrawIndirect ||
else if(n.indirectPatch.type == VkIndirectPatchType::DrawIndirectByteCount ||
n.indirectPatch.type == VkIndirectPatchType::DrawIndirect ||
n.indirectPatch.type == VkIndirectPatchType::DrawIndexedIndirect ||
n.indirectPatch.type == VkIndirectPatchType::DrawIndirectCount ||
n.indirectPatch.type == VkIndirectPatchType::DrawIndexedIndirectCount)
@@ -598,6 +610,16 @@ void WrappedVulkan::InsertDrawsAndRefreshIDs(vector<VulkanDrawcallTreeNode> &cmd
{
bool valid = PatchIndirectDraw(n.indirectPatch.type, n.draw, ptr, end);
if(n.indirectPatch.type == VkIndirectPatchType::DrawIndirectByteCount)
{
if(n.draw.numIndices > n.indirectPatch.vertexoffset)
n.draw.numIndices -= n.indirectPatch.vertexoffset;
else
n.draw.numIndices = 0;
n.draw.numIndices /= n.indirectPatch.stride;
}
if(valid)
n.draw.name =
StringFormat::Fmt("%s(%u) => <%u, %u>", n.draw.name.c_str(), n.indirectPatch.count,
+22 -1
View File
@@ -1924,6 +1924,27 @@ void DoSerialise(SerialiserType &ser, VKPipe::Tessellation &el)
SIZE_CHECK(8);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VKPipe::XFBBuffer &el)
{
SERIALISE_MEMBER(active);
SERIALISE_MEMBER(bufferResourceId);
SERIALISE_MEMBER(byteOffset);
SERIALISE_MEMBER(byteSize);
SERIALISE_MEMBER(counterBufferResourceId);
SERIALISE_MEMBER(counterBufferOffset);
SIZE_CHECK(16);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VKPipe::TransformFeedback &el)
{
SERIALISE_MEMBER(buffers);
SIZE_CHECK(16);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VKPipe::ViewportScissor &el)
{
@@ -2118,7 +2139,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::State &el)
SERIALISE_MEMBER(images);
SIZE_CHECK(1344);
SIZE_CHECK(1360);
}
#pragma endregion Vulkan pipeline state