diff --git a/docs/python_api/renderdoc/outputs.rst b/docs/python_api/renderdoc/outputs.rst index 7ba929596..07b209c1c 100644 --- a/docs/python_api/renderdoc/outputs.rst +++ b/docs/python_api/renderdoc/outputs.rst @@ -52,6 +52,12 @@ Mesh View .. autoclass:: MeshDataStage :members: +.. autoclass:: MeshletSize + :members: + +.. autoclass:: TaskGroupSize + :members: + .. autoclass:: MeshFormat :members: @@ -67,4 +73,4 @@ Mesh View .. autoclass:: AxisMapping :members: -.. autofunction:: renderdoc.InitCamera \ No newline at end of file +.. autofunction:: renderdoc.InitCamera diff --git a/docs/python_api/renderdoc/pipelines/common.rst b/docs/python_api/renderdoc/pipelines/common.rst index be896362b..1cc1f7bdd 100644 --- a/docs/python_api/renderdoc/pipelines/common.rst +++ b/docs/python_api/renderdoc/pipelines/common.rst @@ -147,6 +147,9 @@ Shader Messages .. autoclass:: renderdoc.ShaderMessageLocation :members: +.. autoclass:: renderdoc.ShaderMeshMessageLocation + :members: + .. autoclass:: renderdoc.ShaderVertexMessageLocation :members: diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 90ccf0e70..eb1aabdea 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -1701,8 +1701,9 @@ void CaptureContext::RefreshUIStatus(const rdcarray &exclude, bool updateSelectedEvent, bool updateEvent) { // cache and assign pointer type IDs for any known pointer types in current shaders - for(ShaderStage stage : {ShaderStage::Vertex, ShaderStage::Hull, ShaderStage::Domain, - ShaderStage::Geometry, ShaderStage::Pixel, ShaderStage::Compute}) + for(ShaderStage stage : + {ShaderStage::Vertex, ShaderStage::Hull, ShaderStage::Domain, ShaderStage::Geometry, + ShaderStage::Pixel, ShaderStage::Compute, ShaderStage::Task, ShaderStage::Mesh}) { const ShaderReflection *refl = m_CurPipelineState->GetShaderReflection(stage); if(refl) diff --git a/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp b/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp index 4bd4684b2..b396fae87 100644 --- a/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp +++ b/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp @@ -29,11 +29,12 @@ #include "QRDInterface.h" static const QString glsl_stage4[arraydim()] = { - lit("vert"), lit("tesc"), lit("tese"), lit("geom"), lit("frag"), lit("comp"), + lit("vert"), lit("tesc"), lit("tese"), lit("geom"), + lit("frag"), lit("comp"), lit("task"), lit("mesh"), }; static const QString hlsl_stage2[arraydim()] = { - lit("vs"), lit("hs"), lit("ds"), lit("gs"), lit("ps"), lit("cs"), + lit("vs"), lit("hs"), lit("ds"), lit("gs"), lit("ps"), lit("cs"), lit("as"), lit("ms"), }; static QString tmpPath(const QString &filename) diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index d271a5f8b..a6a7b2a63 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -1378,31 +1378,37 @@ QString ToQStr(const ResourceUsage usage, const GraphicsAPI apitype) case ResourceUsage::VertexBuffer: return lit("Vertex Buffer"); case ResourceUsage::IndexBuffer: return lit("Index Buffer"); - case ResourceUsage::VS_Constants: return lit("VS - Uniform Buffer"); - case ResourceUsage::GS_Constants: return lit("GS - Uniform Buffer"); - case ResourceUsage::HS_Constants: return lit("HS - Uniform Buffer"); - case ResourceUsage::DS_Constants: return lit("DS - Uniform Buffer"); - case ResourceUsage::CS_Constants: return lit("CS - Uniform Buffer"); - case ResourceUsage::PS_Constants: return lit("PS - Uniform Buffer"); - case ResourceUsage::All_Constants: return lit("All - Uniform Buffer"); + case ResourceUsage::VS_Constants: return lit("VS - Constant Buffer"); + case ResourceUsage::GS_Constants: return lit("GS - Constant Buffer"); + case ResourceUsage::HS_Constants: return lit("TCS - Constant Buffer"); + case ResourceUsage::DS_Constants: return lit("TES - Constant Buffer"); + case ResourceUsage::PS_Constants: return lit("FS - Constant Buffer"); + case ResourceUsage::CS_Constants: return lit("CS - Constant Buffer"); + case ResourceUsage::TS_Constants: return lit("TS - Constant Buffer"); + case ResourceUsage::MS_Constants: return lit("MS - Constant Buffer"); + case ResourceUsage::All_Constants: return lit("All - Constant Buffer"); case ResourceUsage::StreamOut: return lit("Stream Out"); - case ResourceUsage::VS_Resource: return lit("VS - Texture"); - case ResourceUsage::GS_Resource: return lit("GS - Texture"); - case ResourceUsage::HS_Resource: return lit("HS - Texture"); - case ResourceUsage::DS_Resource: return lit("DS - Texture"); - case ResourceUsage::CS_Resource: return lit("CS - Texture"); - case ResourceUsage::PS_Resource: return lit("PS - Texture"); - case ResourceUsage::All_Resource: return lit("All - Texture"); + case ResourceUsage::VS_Resource: return lit("VS - Resource"); + case ResourceUsage::GS_Resource: return lit("GS - Resource"); + case ResourceUsage::HS_Resource: return lit("TCS - Resource"); + case ResourceUsage::DS_Resource: return lit("TES - Resource"); + case ResourceUsage::PS_Resource: return lit("FS - Resource"); + case ResourceUsage::CS_Resource: return lit("CS - Resource"); + case ResourceUsage::TS_Resource: return lit("TS - Resource"); + case ResourceUsage::MS_Resource: return lit("MS - Resource"); + case ResourceUsage::All_Resource: return lit("All - Resource"); - case ResourceUsage::VS_RWResource: return lit("VS - Image/SSBO"); - case ResourceUsage::HS_RWResource: return lit("HS - Image/SSBO"); - case ResourceUsage::DS_RWResource: return lit("DS - Image/SSBO"); - case ResourceUsage::GS_RWResource: return lit("GS - Image/SSBO"); - case ResourceUsage::PS_RWResource: return lit("PS - Image/SSBO"); - case ResourceUsage::CS_RWResource: return lit("CS - Image/SSBO"); - case ResourceUsage::All_RWResource: return lit("All - Image/SSBO"); + case ResourceUsage::VS_RWResource: return lit("VS - UAV"); + case ResourceUsage::HS_RWResource: return lit("TCS - UAV"); + case ResourceUsage::DS_RWResource: return lit("TES - UAV"); + case ResourceUsage::GS_RWResource: return lit("GS - UAV"); + case ResourceUsage::PS_RWResource: return lit("FS - UAV"); + case ResourceUsage::CS_RWResource: return lit("CS - UAV"); + case ResourceUsage::TS_RWResource: return lit("TS - UAV"); + case ResourceUsage::MS_RWResource: return lit("MS - UAV"); + case ResourceUsage::All_RWResource: return lit("All - UAV"); case ResourceUsage::InputTarget: return lit("Color Input"); case ResourceUsage::ColorTarget: return lit("Rendertarget"); @@ -1437,31 +1443,37 @@ QString ToQStr(const ResourceUsage usage, const GraphicsAPI apitype) case ResourceUsage::VertexBuffer: return lit("Vertex Buffer"); case ResourceUsage::IndexBuffer: return lit("Index Buffer"); - case ResourceUsage::VS_Constants: return lit("VS - Constant Buffer"); - case ResourceUsage::GS_Constants: return lit("GS - Constant Buffer"); - case ResourceUsage::HS_Constants: return lit("TCS - Constant Buffer"); - case ResourceUsage::DS_Constants: return lit("TES - Constant Buffer"); - case ResourceUsage::CS_Constants: return lit("CS - Constant Buffer"); - case ResourceUsage::PS_Constants: return lit("FS - Constant Buffer"); - case ResourceUsage::All_Constants: return lit("All - Constant Buffer"); + case ResourceUsage::VS_Constants: return lit("VS - Uniform Buffer"); + case ResourceUsage::GS_Constants: return lit("GS - Uniform Buffer"); + case ResourceUsage::HS_Constants: return lit("HS - Uniform Buffer"); + case ResourceUsage::DS_Constants: return lit("DS - Uniform Buffer"); + case ResourceUsage::PS_Constants: return lit("PS - Uniform Buffer"); + case ResourceUsage::CS_Constants: return lit("CS - Uniform Buffer"); + case ResourceUsage::TS_Constants: return lit("TS - Uniform Buffer"); + case ResourceUsage::MS_Constants: return lit("MS - Uniform Buffer"); + case ResourceUsage::All_Constants: return lit("All - Uniform Buffer"); case ResourceUsage::StreamOut: return lit("Transform Feedback"); - case ResourceUsage::VS_Resource: return lit("VS - Resource"); - case ResourceUsage::GS_Resource: return lit("GS - Resource"); - case ResourceUsage::HS_Resource: return lit("TCS - Resource"); - case ResourceUsage::DS_Resource: return lit("TES - Resource"); - case ResourceUsage::CS_Resource: return lit("CS - Resource"); - case ResourceUsage::PS_Resource: return lit("FS - Resource"); - case ResourceUsage::All_Resource: return lit("All - Resource"); + case ResourceUsage::VS_Resource: return lit("VS - Texture"); + case ResourceUsage::GS_Resource: return lit("GS - Texture"); + case ResourceUsage::HS_Resource: return lit("HS - Texture"); + case ResourceUsage::DS_Resource: return lit("DS - Texture"); + case ResourceUsage::PS_Resource: return lit("PS - Texture"); + case ResourceUsage::CS_Resource: return lit("CS - Texture"); + case ResourceUsage::TS_Resource: return lit("TS - Texture"); + case ResourceUsage::MS_Resource: return lit("MS - Texture"); + case ResourceUsage::All_Resource: return lit("All - Texture"); - case ResourceUsage::VS_RWResource: return lit("VS - UAV"); - case ResourceUsage::HS_RWResource: return lit("TCS - UAV"); - case ResourceUsage::DS_RWResource: return lit("TES - UAV"); - case ResourceUsage::GS_RWResource: return lit("GS - UAV"); - case ResourceUsage::PS_RWResource: return lit("FS - UAV"); - case ResourceUsage::CS_RWResource: return lit("CS - UAV"); - case ResourceUsage::All_RWResource: return lit("All - UAV"); + case ResourceUsage::VS_RWResource: return lit("VS - Image/SSBO"); + case ResourceUsage::HS_RWResource: return lit("HS - Image/SSBO"); + case ResourceUsage::DS_RWResource: return lit("DS - Image/SSBO"); + case ResourceUsage::GS_RWResource: return lit("GS - Image/SSBO"); + case ResourceUsage::PS_RWResource: return lit("PS - Image/SSBO"); + case ResourceUsage::CS_RWResource: return lit("CS - Image/SSBO"); + case ResourceUsage::TS_RWResource: return lit("TS - Image/SSBO"); + case ResourceUsage::MS_RWResource: return lit("MS - Image/SSBO"); + case ResourceUsage::All_RWResource: return lit("All - Image/SSBO"); case ResourceUsage::InputTarget: return lit("FB Input"); case ResourceUsage::ColorTarget: return lit("FB Color"); @@ -1503,6 +1515,8 @@ QString ToQStr(const ShaderStage stage, const GraphicsAPI apitype) case ShaderStage::Geometry: return lit("Geometry"); case ShaderStage::Pixel: return lit("Pixel"); case ShaderStage::Compute: return lit("Compute"); + case ShaderStage::Amplification: return lit("Amplif."); + case ShaderStage::Mesh: return lit("Mesh"); default: break; } } @@ -1516,6 +1530,8 @@ QString ToQStr(const ShaderStage stage, const GraphicsAPI apitype) case ShaderStage::Geometry: return lit("Geometry"); case ShaderStage::Fragment: return lit("Fragment"); case ShaderStage::Compute: return lit("Compute"); + case ShaderStage::Task: return lit("Task"); + case ShaderStage::Mesh: return lit("Mesh"); default: break; } } @@ -1636,6 +1652,7 @@ QString D3DSemanticString(const SigParameter &sig) lit("SV_ShadingRate"), lit("SV_Barycentrics"), lit("SV_CullPrimitive"), + lit("out indices"), }; static_assert(arraydim() == ARRAY_COUNT(sysValues), @@ -1705,7 +1722,8 @@ void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, while(prev != NULL && prev->eventId > end) { - if(!(prev->flags & (ActionFlags::Dispatch | ActionFlags::Drawcall | ActionFlags::CmdList))) + if(!(prev->flags & (ActionFlags::Dispatch | ActionFlags::MeshDispatch | + ActionFlags::Drawcall | ActionFlags::CmdList))) { prev = prev->previous; } diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 67831228e..97da4a1cd 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -370,6 +370,8 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, EnvironmentModification) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, EventUsage) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, PathEntry) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, PixelModification) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, TaskGroupSize) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, MeshletSize) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ResourceDescription) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ResourceId) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, LineColumnInfo) diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index c8db1a46e..50c89e402 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -1473,11 +1473,16 @@ struct PopulateBufferData CBufferData cb; + // {VSIn, VSOut, GSOut} x {primary, secondary} QString highlightNames[6]; - BufferConfiguration vsinConfig, vsoutConfig, gsoutConfig; + bool meshDispatch = false; + BufferConfiguration vsinConfig, vsoutConfig, gsoutConfig; MeshFormat postVS, postGS; + + BufferConfiguration msoutConfig; + MeshFormat postMS; }; struct CalcBoundingBoxData @@ -1564,10 +1569,15 @@ static void ConfigureColumnsForShader(ICaptureContext &ctx, int32_t streamSelect if(sig.stream != (uint32_t)streamSelect) continue; + if(sig.systemValue == ShaderBuiltin::OutputIndices) + continue; + ShaderConstant f; BufferElementProperties p; f.name = !sig.varName.isEmpty() ? sig.varName : sig.semanticIdxName; + if(sig.perPrimitiveRate) + f.name += lit(" (Per-Prim)"); f.type.rows = 1; f.type.columns = sig.compCount; @@ -1624,7 +1634,20 @@ static void ConfigureColumnsForShader(ICaptureContext &ctx, int32_t streamSelect } } -static void ConfigureMeshColumns(ICaptureContext &ctx, PopulateBufferData *bufdata) +static void ConfigureColumnsForMeshPipe(ICaptureContext &ctx, PopulateBufferData *bufdata) +{ + bufdata->vsinConfig.numRows = 0; + bufdata->vsinConfig.unclampedNumRows = 0; + + bufdata->vsinConfig.noVertices = true; + bufdata->vsinConfig.noInstances = true; + + const ShaderReflection *ms = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Mesh); + + ConfigureColumnsForShader(ctx, 0, ms, bufdata->vsoutConfig.columns, bufdata->vsoutConfig.props); +} + +static void ConfigureColumnsForVertexPipe(ICaptureContext &ctx, PopulateBufferData *bufdata) { const ActionDescription *action = ctx.CurAction(); @@ -1634,28 +1657,6 @@ static void ConfigureMeshColumns(ICaptureContext &ctx, PopulateBufferData *bufda bufdata->vsinConfig.noVertices = false; bufdata->vsinConfig.noInstances = false; - if(!action || !(action->flags & ActionFlags::Drawcall)) - { - IEventBrowser *eb = ctx.GetEventBrowser(); - - bufdata->vsinConfig.statusString = bufdata->vsoutConfig.statusString = - bufdata->gsoutConfig.statusString = - lit("No current draw action\nSelected EID @%1 - %2\nEffective EID: @%3 - %4") - .arg(ctx.CurSelectedEvent()) - .arg(QString(eb->GetEventName(ctx.CurSelectedEvent()))) - .arg(ctx.CurEvent()) - .arg(QString(eb->GetEventName(ctx.CurEvent()))); - - ConfigureStatusColumn(bufdata->vsinConfig.columns, bufdata->vsinConfig.props); - ConfigureStatusColumn(bufdata->vsoutConfig.columns, bufdata->vsoutConfig.props); - ConfigureStatusColumn(bufdata->gsoutConfig.columns, bufdata->gsoutConfig.props); - - bufdata->vsinConfig.genericsEnabled.push_back(false); - bufdata->vsinConfig.generics.push_back(PixelValue()); - - return; - } - rdcarray vinputs = ctx.CurPipelineState().GetVertexInputs(); bufdata->vsinConfig.columns.reserve(vinputs.count()); @@ -1696,33 +1697,63 @@ static void ConfigureMeshColumns(ICaptureContext &ctx, PopulateBufferData *bufda bufdata->vsinConfig.props.push_back(p); } - if(action) + bufdata->vsinConfig.numRows = action->numIndices; + bufdata->vsinConfig.unclampedNumRows = 0; + + // calculate an upper bound on the valid number of rows just in case it's an invalid value (e.g. + // 0xdeadbeef) and we want to clamp. + uint32_t numRowsUpperBound = 0; + + if(action->flags & ActionFlags::Indexed) { - bufdata->vsinConfig.numRows = action->numIndices; - bufdata->vsinConfig.unclampedNumRows = 0; + // In an indexed draw we clamp to however many indices are available in the index buffer - // calculate an upper bound on the valid number of rows just in case it's an invalid value (e.g. - // 0xdeadbeef) and we want to clamp. - uint32_t numRowsUpperBound = 0; + BoundVBuffer ib = ctx.CurPipelineState().GetIBuffer(); - if(action->flags & ActionFlags::Indexed) + uint32_t bytesAvailable = ib.byteSize; + + if(bytesAvailable == ~0U) { - // In an indexed draw we clamp to however many indices are available in the index buffer + BufferDescription *buf = ctx.GetBuffer(ib.resourceId); + if(buf) + { + uint64_t offset = ib.byteOffset + action->indexOffset * ib.byteStride; + if(offset > buf->length) + bytesAvailable = 0; + else + bytesAvailable = buf->length - offset; + } + else + { + bytesAvailable = 0; + } + } - BoundVBuffer ib = ctx.CurPipelineState().GetIBuffer(); + // drawing more than this many indices will read off the end of the index buffer - which while + // technically not invalid is certainly not intended, so serves as a good 'upper bound' + numRowsUpperBound = bytesAvailable / qMax(1U, ib.byteStride); + } + else + { + // for a non-indexed draw, we take the largest vertex buffer + rdcarray VBs = ctx.CurPipelineState().GetVBuffers(); - uint32_t bytesAvailable = ib.byteSize; + for(const BoundVBuffer &vb : VBs) + { + if(vb.byteStride == 0) + continue; + + uint32_t bytesAvailable = vb.byteSize; if(bytesAvailable == ~0U) { - BufferDescription *buf = ctx.GetBuffer(ib.resourceId); + BufferDescription *buf = ctx.GetBuffer(vb.resourceId); if(buf) { - uint64_t offset = ib.byteOffset + action->indexOffset * ib.byteStride; - if(offset > buf->length) + if(vb.byteOffset > buf->length) bytesAvailable = 0; else - bytesAvailable = buf->length - offset; + bytesAvailable = buf->length - vb.byteOffset; } else { @@ -1730,61 +1761,28 @@ static void ConfigureMeshColumns(ICaptureContext &ctx, PopulateBufferData *bufda } } - // drawing more than this many indices will read off the end of the index buffer - which while - // technically not invalid is certainly not intended, so serves as a good 'upper bound' - numRowsUpperBound = bytesAvailable / qMax(1U, ib.byteStride); - } - else - { - // for a non-indexed draw, we take the largest vertex buffer - rdcarray VBs = ctx.CurPipelineState().GetVBuffers(); - - for(const BoundVBuffer &vb : VBs) - { - if(vb.byteStride == 0) - continue; - - uint32_t bytesAvailable = vb.byteSize; - - if(bytesAvailable == ~0U) - { - BufferDescription *buf = ctx.GetBuffer(vb.resourceId); - if(buf) - { - if(vb.byteOffset > buf->length) - bytesAvailable = 0; - else - bytesAvailable = buf->length - vb.byteOffset; - } - else - { - bytesAvailable = 0; - } - } - - numRowsUpperBound = qMax(numRowsUpperBound, bytesAvailable / qMax(1U, vb.byteStride)); - } - - // if there are no vertex buffers we can't clamp. - if(numRowsUpperBound == 0) - numRowsUpperBound = ~0U; + numRowsUpperBound = qMax(numRowsUpperBound, bytesAvailable / qMax(1U, vb.byteStride)); } - // if we have significantly clamped, then set the unclamped number of rows and clamp. - if(numRowsUpperBound != ~0U && numRowsUpperBound + 100 < bufdata->vsinConfig.numRows) - { - bufdata->vsinConfig.unclampedNumRows = bufdata->vsinConfig.numRows; - bufdata->vsinConfig.numRows = numRowsUpperBound + 100; - } + // if there are no vertex buffers we can't clamp. + if(numRowsUpperBound == 0) + numRowsUpperBound = ~0U; + } - if((action->flags & ActionFlags::Drawcall) && action->numIndices == 0) - bufdata->vsinConfig.noVertices = true; + // if we have significantly clamped, then set the unclamped number of rows and clamp. + if(numRowsUpperBound != ~0U && numRowsUpperBound + 100 < bufdata->vsinConfig.numRows) + { + bufdata->vsinConfig.unclampedNumRows = bufdata->vsinConfig.numRows; + bufdata->vsinConfig.numRows = numRowsUpperBound + 100; + } - if((action->flags & ActionFlags::Instanced) && action->numInstances == 0) - { - bufdata->vsinConfig.noInstances = true; - bufdata->vsinConfig.numRows = bufdata->vsinConfig.unclampedNumRows = 0; - } + if((action->flags & ActionFlags::Drawcall) && action->numIndices == 0) + bufdata->vsinConfig.noVertices = true; + + if((action->flags & ActionFlags::Instanced) && action->numInstances == 0) + { + bufdata->vsinConfig.noInstances = true; + bufdata->vsinConfig.numRows = bufdata->vsinConfig.unclampedNumRows = 0; } bufdata->vsoutConfig.columns.clear(); @@ -1792,16 +1790,46 @@ static void ConfigureMeshColumns(ICaptureContext &ctx, PopulateBufferData *bufda bufdata->gsoutConfig.columns.clear(); bufdata->gsoutConfig.props.clear(); - if(action) - { - const ShaderReflection *vs = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Vertex); - const ShaderReflection *last = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Geometry); - if(last == NULL) - last = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Domain); + const ShaderReflection *vs = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Vertex); + const ShaderReflection *last = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Geometry); + if(last == NULL) + last = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Domain); - ConfigureColumnsForShader(ctx, 0, vs, bufdata->vsoutConfig.columns, bufdata->vsoutConfig.props); - ConfigureColumnsForShader(ctx, ctx.CurPipelineState().GetRasterizedStream(), last, - bufdata->gsoutConfig.columns, bufdata->gsoutConfig.props); + ConfigureColumnsForShader(ctx, 0, vs, bufdata->vsoutConfig.columns, bufdata->vsoutConfig.props); + ConfigureColumnsForShader(ctx, ctx.CurPipelineState().GetRasterizedStream(), last, + bufdata->gsoutConfig.columns, bufdata->gsoutConfig.props); +} + +static void ConfigureColumns(ICaptureContext &ctx, PopulateBufferData *bufdata) +{ + const ActionDescription *action = ctx.CurAction(); + + if(action && (action->flags & ActionFlags::MeshDispatch)) + { + ConfigureColumnsForMeshPipe(ctx, bufdata); + } + else if(action && (action->flags & ActionFlags::Drawcall)) + { + ConfigureColumnsForVertexPipe(ctx, bufdata); + } + else + { + IEventBrowser *eb = ctx.GetEventBrowser(); + + bufdata->vsinConfig.statusString = bufdata->vsoutConfig.statusString = + bufdata->gsoutConfig.statusString = + lit("No current draw action\nSelected EID @%1 - %2\nEffective EID: @%3 - %4") + .arg(ctx.CurSelectedEvent()) + .arg(QString(eb->GetEventName(ctx.CurSelectedEvent()))) + .arg(ctx.CurEvent()) + .arg(QString(eb->GetEventName(ctx.CurEvent()))); + + ConfigureStatusColumn(bufdata->vsinConfig.columns, bufdata->vsinConfig.props); + ConfigureStatusColumn(bufdata->vsoutConfig.columns, bufdata->vsoutConfig.props); + ConfigureStatusColumn(bufdata->gsoutConfig.columns, bufdata->gsoutConfig.props); + + bufdata->vsinConfig.genericsEnabled.push_back(false); + bufdata->vsinConfig.generics.push_back(PixelValue()); } } @@ -2860,7 +2888,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId) // GS Out doesn't use primitive restart because it is post-expansion } - ConfigureMeshColumns(m_Ctx, bufdata); + ConfigureColumns(m_Ctx, bufdata); Viewport vp = m_Ctx.CurPipelineState().GetViewport(0); diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h index bfb0d3b29..98235b789 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h @@ -132,7 +132,7 @@ private: Ui::PipelineStateViewer *ui; ICaptureContext &m_Ctx; - QMenu *editMenus[6] = {}; + QMenu *editMenus[NumShaderStages] = {}; RDPreviewTooltip *m_Tooltip = NULL; diff --git a/qrenderdoc/Windows/ShaderMessageViewer.cpp b/qrenderdoc/Windows/ShaderMessageViewer.cpp index ee9c1d69f..5af3b383c 100644 --- a/qrenderdoc/Windows/ShaderMessageViewer.cpp +++ b/qrenderdoc/Windows/ShaderMessageViewer.cpp @@ -144,6 +144,8 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s m_API = m_Ctx.APIProps().pipelineType; + QObject::connect(ui->task, &QToolButton::toggled, [this](bool) { refreshMessages(); }); + QObject::connect(ui->mesh, &QToolButton::toggled, [this](bool) { refreshMessages(); }); QObject::connect(ui->vertex, &QToolButton::toggled, [this](bool) { refreshMessages(); }); QObject::connect(ui->hull, &QToolButton::toggled, [this](bool) { refreshMessages(); }); QObject::connect(ui->domain, &QToolButton::toggled, [this](bool) { refreshMessages(); }); @@ -167,6 +169,8 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s ui->exportButton->setMenu(menu); QObject::connect(ui->exportButton, &QToolButton::clicked, this, &ShaderMessageViewer::exportText); + ui->task->setText(ToQStr(ShaderStage::Task, m_API)); + ui->mesh->setText(ToQStr(ShaderStage::Mesh, m_API)); ui->vertex->setText(ToQStr(ShaderStage::Vertex, m_API)); ui->hull->setText(ToQStr(ShaderStage::Hull, m_API)); ui->domain->setText(ToQStr(ShaderStage::Domain, m_API)); @@ -219,11 +223,30 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s ui->messages->setItemDelegateForColumn(0, m_debugDelegate); m_OrigShaders[5] = pipe.GetShader(ShaderStage::Compute); + m_LayoutStage = ShaderStage::Compute; } else { - ui->messages->setColumns({lit("Debug"), lit("Go to"), tr("Location"), lit("Message")}); - sortColumn = 2; + if(pipe.GetShader(ShaderStage::Task) != ResourceId()) + { + ui->messages->setColumns({lit("Debug"), lit("Go to"), tr("Task group"), tr("Mesh group"), + lit("Thread"), lit("Message")}); + sortColumn = 4; + m_LayoutStage = ShaderStage::Task; + } + else if(pipe.GetShader(ShaderStage::Mesh) != ResourceId()) + { + ui->messages->setColumns( + {lit("Debug"), lit("Go to"), tr("Workgroup"), lit("Thread/Location"), lit("Message")}); + sortColumn = 3; + m_LayoutStage = ShaderStage::Mesh; + } + else + { + ui->messages->setColumns({lit("Debug"), lit("Go to"), tr("Location"), lit("Message")}); + sortColumn = 2; + m_LayoutStage = ShaderStage::Vertex; + } m_gotoDelegate = new ButtonDelegate(Icons::find(), gotoableRole, this); @@ -231,7 +254,15 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s ui->messages->setItemDelegateForColumn(1, m_gotoDelegate); QCheckBox *boxes[] = { - ui->vertex, ui->hull, ui->domain, ui->geometry, ui->pixel, + ui->vertex, + ui->hull, + ui->domain, + ui->geometry, + ui->pixel, + // compute + NULL, + ui->task, + ui->mesh, }; for(ShaderStage s : values()) @@ -314,7 +345,7 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s trace = r->DebugPixel(msg.location.pixel.x, msg.location.pixel.y, msg.location.pixel.sample, msg.location.pixel.primitive); - if(trace->debugger == NULL) + if(trace && trace->debugger == NULL) { r->FreeTrace(trace); trace = NULL; @@ -326,6 +357,7 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s QString debugContext; if(msg.stage == ShaderStage::Compute) + { debugContext = lit("Group [%1,%2,%3] Thread [%4,%5,%6]") .arg(msg.location.compute.workgroup[0]) .arg(msg.location.compute.workgroup[1]) @@ -333,10 +365,57 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s .arg(msg.location.compute.thread[0]) .arg(msg.location.compute.thread[1]) .arg(msg.location.compute.thread[2]); + } else if(msg.stage == ShaderStage::Vertex) + { debugContext = tr("Vertex %1").arg(msg.location.vertex.vertexIndex); + } else if(msg.stage == ShaderStage::Pixel) + { debugContext = tr("Pixel %1,%2").arg(msg.location.pixel.x).arg(msg.location.pixel.y); + } + else if(msg.stage == ShaderStage::Task) + { + QString groupIdx = QFormatStr("%1").arg(msg.location.mesh.taskGroup[0]); + if(msg.location.mesh.taskGroup[1] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.taskGroup[1]); + if(msg.location.mesh.taskGroup[2] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.taskGroup[2]); + + QString threadIdx = QFormatStr("%1").arg(msg.location.mesh.thread[0]); + if(msg.location.mesh.thread[1] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[1]); + if(msg.location.mesh.thread[2] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[2]); + + debugContext = tr("Task Group [%1] Thread [%2]").arg(groupIdx).arg(threadIdx); + } + else if(msg.stage == ShaderStage::Mesh) + { + QString groupIdx = QFormatStr("%1").arg(msg.location.mesh.meshGroup[0]); + if(msg.location.mesh.meshGroup[1] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.meshGroup[1]); + if(msg.location.mesh.meshGroup[2] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.meshGroup[2]); + + QString threadIdx = QFormatStr("%1").arg(msg.location.mesh.thread[0]); + if(msg.location.mesh.thread[1] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[1]); + if(msg.location.mesh.thread[2] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[2]); + + debugContext = tr("Mesh Group [%1] Thread [%2]").arg(groupIdx).arg(threadIdx); + + if(msg.location.mesh.taskGroup[0] != ShaderMeshMessageLocation::NotUsed) + { + debugContext += tr(" from Task [%1").arg(msg.location.mesh.taskGroup[0]); + if(msg.location.mesh.taskGroup[1] != ShaderMeshMessageLocation::NotUsed) + debugContext += tr(",%1").arg(msg.location.mesh.taskGroup[1]); + if(msg.location.mesh.taskGroup[2] != ShaderMeshMessageLocation::NotUsed) + debugContext += tr(",%1").arg(msg.location.mesh.taskGroup[2]); + debugContext += lit("]"); + } + } // wait a short while before displaying the progress dialog (which won't show if we're already // done by the time we reach it) @@ -436,6 +515,10 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s msg.location.geometry.primitive), MeshDataStage::GSOut); } + else if(msg.stage == ShaderStage::Task || msg.stage == ShaderStage::Mesh) + { + // TODO mesh shader jumping + } else { qCritical() << "Can't go to a compute thread"; @@ -472,10 +555,43 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s const ShaderMessage &am = m_Messages[a->tag().toInt()]; const ShaderMessage &bm = m_Messages[b->tag().toInt()]; - if(col == 3) + if(col == 5) { + // column 5 is the message when task shaders are used return am.message < bm.message; } + else if(col == 4) + { + // column 4 is the message, except when task shaders are used - then it's + // the thread index + if((am.stage == ShaderStage::Task || am.stage == ShaderStage::Mesh) && + am.location.mesh.taskGroup[0] != ShaderMeshMessageLocation::NotUsed) + { + return am.location.mesh.thread < bm.location.mesh.thread; + } + else + { + return am.message < bm.message; + } + } + else if(col == 3) + { + // column 3 is the mesh thread when only mesh shaders are used, or the mesh group when + // task shaders are used. For non mesh/task it is the message + if((am.stage == ShaderStage::Task || am.stage == ShaderStage::Mesh) && + am.location.mesh.taskGroup[0] != ShaderMeshMessageLocation::NotUsed) + { + return am.location.mesh.meshGroup < bm.location.mesh.meshGroup; + } + else if(am.stage == ShaderStage::Mesh) + { + return am.location.mesh.thread < bm.location.mesh.thread; + } + else + { + return am.message < bm.message; + } + } else if(col == 2 || m_OrigShaders[5] == ResourceId()) { // sort by location either if it's selected, or if it's not dispatch in which case we @@ -517,6 +633,15 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s // column 2 is the thread column for compute return am.location.compute.thread < bm.location.compute.thread; } + else if(am.stage == ShaderStage::Task || am.stage == ShaderStage::Mesh) + { + // column 2 is the mesh group column, or the task group column, depending on if + // task shaders were in use + if(am.location.mesh.taskGroup[0] != ShaderMeshMessageLocation::NotUsed) + return am.location.mesh.taskGroup < bm.location.mesh.taskGroup; + else + return am.location.mesh.meshGroup < bm.location.mesh.meshGroup; + } else if(am.stage == ShaderStage::Geometry) { const ShaderGeometryMessageLocation &aloc = am.location.geometry; @@ -571,7 +696,7 @@ void ShaderMessageViewer::OnCaptureClosed() void ShaderMessageViewer::OnEventChanged(uint32_t eventId) { - ResourceId shaders[6]; + ResourceId shaders[NumShaderStages]; bool editsChanged = false; QString staleReason; @@ -740,6 +865,10 @@ void ShaderMessageViewer::refreshMessages() { mask = ShaderStageMask::Unknown; + if(ui->task->isChecked()) + mask |= ShaderStageMask::Task; + if(ui->mesh->isChecked()) + mask |= ShaderStageMask::Mesh; if(ui->vertex->isChecked()) mask |= ShaderStageMask::Vertex; if(ui->hull->isChecked()) @@ -768,6 +897,8 @@ void ShaderMessageViewer::refreshMessages() const ShaderReflection *vsrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Vertex); const ShaderReflection *psrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel); const ShaderReflection *csrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); + const ShaderReflection *tsrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Task); + const ShaderReflection *msrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Mesh); for(int i = 0; i < m_Messages.count(); i++) { @@ -846,6 +977,14 @@ void ShaderMessageViewer::refreshMessages() location += lit(", View %1").arg(msg.location.geometry.view); } } + else if(msg.stage == ShaderStage::Task) + { + refl = tsrefl; + } + else if(msg.stage == ShaderStage::Mesh) + { + refl = msrefl; + } else { // no location info for other stages @@ -876,9 +1015,83 @@ void ShaderMessageViewer::refreshMessages() node->setData(0, debuggableRole, refl && refl->debugInfo.debuggable); } + else if(msg.stage == ShaderStage::Task) + { + QString groupIdx = QFormatStr("%1").arg(msg.location.mesh.taskGroup[0]); + if(msg.location.mesh.taskGroup[1] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.taskGroup[1]); + if(msg.location.mesh.taskGroup[2] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.taskGroup[2]); + + QString threadIdx = QFormatStr("%1").arg(msg.location.mesh.thread[0]); + if(msg.location.mesh.thread[1] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[1]); + if(msg.location.mesh.thread[2] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[2]); + + node = new RDTreeWidgetItem({ + QString(), + QString(), + groupIdx, + lit("-"), + threadIdx, + text, + }); + + node->setData(0, debuggableRole, refl && refl->debugInfo.debuggable); + node->setData(1, gotoableRole, true); + } + else if(msg.stage == ShaderStage::Mesh) + { + QString taskIdx; + if(msg.location.mesh.taskGroup[0] != ShaderMeshMessageLocation::NotUsed) + taskIdx = QFormatStr("%1").arg(msg.location.mesh.taskGroup[0]); + if(msg.location.mesh.taskGroup[1] != ShaderMeshMessageLocation::NotUsed) + taskIdx += QFormatStr(",%1").arg(msg.location.mesh.taskGroup[1]); + if(msg.location.mesh.taskGroup[2] != ShaderMeshMessageLocation::NotUsed) + taskIdx += QFormatStr(",%1").arg(msg.location.mesh.taskGroup[2]); + + QString groupIdx = QFormatStr("%1").arg(msg.location.mesh.meshGroup[0]); + if(msg.location.mesh.meshGroup[1] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.meshGroup[1]); + if(msg.location.mesh.meshGroup[2] != ShaderMeshMessageLocation::NotUsed) + groupIdx += QFormatStr(",%1").arg(msg.location.mesh.meshGroup[2]); + + QString threadIdx = QFormatStr("%1").arg(msg.location.mesh.thread[0]); + if(msg.location.mesh.thread[1] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[1]); + if(msg.location.mesh.thread[2] != ShaderMeshMessageLocation::NotUsed) + threadIdx += QFormatStr(",%1").arg(msg.location.mesh.thread[2]); + + if(m_LayoutStage == ShaderStage::Task) + node = new RDTreeWidgetItem({ + QString(), + QString(), + taskIdx, + groupIdx, + threadIdx, + text, + }); + else + node = new RDTreeWidgetItem({ + QString(), + QString(), + groupIdx, + threadIdx, + text, + }); + + node->setData(0, debuggableRole, refl && refl->debugInfo.debuggable); + node->setData(1, gotoableRole, true); + } else { - node = new RDTreeWidgetItem({QString(), QString(), location, text}); + if(m_LayoutStage == ShaderStage::Task) + node = new RDTreeWidgetItem({QString(), QString(), QString(), QString(), location, text}); + else if(m_LayoutStage == ShaderStage::Mesh) + node = new RDTreeWidgetItem({QString(), QString(), QString(), location, text}); + else + node = new RDTreeWidgetItem({QString(), QString(), location, text}); node->setData(0, debuggableRole, refl && refl->debugInfo.debuggable); node->setData(1, gotoableRole, diff --git a/qrenderdoc/Windows/ShaderMessageViewer.h b/qrenderdoc/Windows/ShaderMessageViewer.h index 875bb14dd..9ce78446e 100644 --- a/qrenderdoc/Windows/ShaderMessageViewer.h +++ b/qrenderdoc/Windows/ShaderMessageViewer.h @@ -94,6 +94,7 @@ private: const ActionDescription *m_Action; rdcarray m_Messages; - ResourceId m_OrigShaders[6]; - ResourceId m_ReplacedShaders[6]; + ShaderStage m_LayoutStage; + ResourceId m_OrigShaders[NumShaderStages]; + ResourceId m_ReplacedShaders[NumShaderStages]; }; diff --git a/qrenderdoc/Windows/ShaderMessageViewer.ui b/qrenderdoc/Windows/ShaderMessageViewer.ui index 8a2aa1394..3c9d59c5b 100644 --- a/qrenderdoc/Windows/ShaderMessageViewer.ui +++ b/qrenderdoc/Windows/ShaderMessageViewer.ui @@ -132,6 +132,26 @@ + + + + Task + + + true + + + + + + + Mesh + + + true + + + diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 853a251de..992ed2f47 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -1109,6 +1109,8 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR if(multipleStreams) name = QFormatStr("Stream %1 : %2").arg(s.stream).arg(name); + if(s.perPrimitiveRate) + name += tr(" (Per-Prim)"); QString semIdx = s.needSemanticIndex ? QString::number(s.semanticIndex) : QString(); diff --git a/qrenderdoc/Windows/StatisticsViewer.cpp b/qrenderdoc/Windows/StatisticsViewer.cpp index 3077fb47d..f2dbe3c6f 100644 --- a/qrenderdoc/Windows/StatisticsViewer.cpp +++ b/qrenderdoc/Windows/StatisticsViewer.cpp @@ -633,7 +633,7 @@ void StatisticsViewer::CountContributingEvents(const ActionDescription &action, if(diagnosticMasked != ActionFlags::NoFlags) diagnosticCount += 1; - if(action.flags & ActionFlags::Drawcall) + if(action.flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall)) drawCount += 1; if(action.flags & ActionFlags::Dispatch) diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index 4f035ea14..daf2ea4e7 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -445,7 +445,8 @@ void TextureViewer::UI_UpdateCachedTexture() const ShaderReflection *shaderDetails = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel); - if(!m_Ctx.CurAction() || !(m_Ctx.CurAction()->flags & ActionFlags::Drawcall)) + if(!m_Ctx.CurAction() || + !(m_Ctx.CurAction()->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall))) { ui->debugPixelContext->setEnabled(false); ui->debugPixelContext->setToolTip(tr("No draw call selected")); @@ -3167,10 +3168,12 @@ void TextureViewer::OnEventChanged(uint32_t eventId) bool copy = false, clear = false, compute = false; Following::GetActionContext(m_Ctx, copy, clear, compute); - ShaderStage stages[] = {ShaderStage::Vertex, ShaderStage::Hull, ShaderStage::Domain, - ShaderStage::Geometry, ShaderStage::Pixel}; + ShaderStage stages[] = { + ShaderStage::Vertex, ShaderStage::Hull, ShaderStage::Domain, ShaderStage::Geometry, + ShaderStage::Pixel, ShaderStage::Task, ShaderStage::Mesh, + }; - int count = 5; + int count = 7; if(compute) { diff --git a/qrenderdoc/Windows/TextureViewer.h b/qrenderdoc/Windows/TextureViewer.h index 213b409b2..d0387d126 100644 --- a/qrenderdoc/Windows/TextureViewer.h +++ b/qrenderdoc/Windows/TextureViewer.h @@ -360,8 +360,8 @@ private: friend struct Following; - rdcarray m_ReadOnlyResources[(uint32_t)ShaderStage::Count]; - rdcarray m_ReadWriteResources[(uint32_t)ShaderStage::Count]; + rdcarray m_ReadOnlyResources[NumShaderStages]; + rdcarray m_ReadWriteResources[NumShaderStages]; QTime m_CustomShaderTimer; int m_CustomShaderWriteTime = 0; diff --git a/renderdoc/api/replay/common_pipestate.h b/renderdoc/api/replay/common_pipestate.h index 30d325963..a7ff24d74 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -529,6 +529,68 @@ be emulated. DECLARE_REFLECTION_STRUCT(VertexInputAttribute); +DOCUMENT(R"(A task or mesh message's location. + +.. data:: NotUsed + + Set for values of task group/thread index when no task shaders were run. + + Also set for values of a mesh group or thread index when that dimensionality is unused. For + example if the shader declares a group dimension of (128,1,1) then the y and z values for + thread index will be indicated as not used. +)"); +struct ShaderMeshMessageLocation +{ + DOCUMENT(""); + ShaderMeshMessageLocation() = default; + ShaderMeshMessageLocation(const ShaderMeshMessageLocation &) = default; + ShaderMeshMessageLocation &operator=(const ShaderMeshMessageLocation &) = default; + + bool operator==(const ShaderMeshMessageLocation &o) const + { + return taskGroup == o.taskGroup && meshGroup == o.meshGroup && thread == o.thread; + } + bool operator<(const ShaderMeshMessageLocation &o) const + { + if(!(taskGroup == o.taskGroup)) + return taskGroup < o.taskGroup; + if(!(meshGroup == o.meshGroup)) + return meshGroup < o.meshGroup; + if(!(thread == o.thread)) + return thread < o.thread; + return false; + } + + DOCUMENT(R"(The task workgroup index between the task dispatch. + +.. note:: + If no task shader is in use, this will be :data:`NotUsed`, :data:`NotUsed`, :data:`NotUsed`. + +:type: Tuple[int,int,int] +)"); + rdcfixedarray taskGroup; + + DOCUMENT(R"(The mesh workgroup index within the dispatch or launching task workgroup. + +:type: Tuple[int,int,int] +)"); + rdcfixedarray meshGroup; + + DOCUMENT(R"(The thread index within the workgroup, either for a task shader or mesh shader. + +.. note:: + Since task shaders can only emit one set of meshes per group, the task thread is not relevant + for mesh shader messages, so this is the thread either for a task or a mesh shader message. + +:type: Tuple[int,int,int] +)"); + rdcfixedarray thread; + + static const uint32_t NotUsed = ~0U; +}; + +DECLARE_REFLECTION_STRUCT(ShaderMeshMessageLocation); + DOCUMENT("A compute shader message's location."); struct ShaderComputeMessageLocation { @@ -664,6 +726,12 @@ union ShaderMessageLocation )"); ShaderComputeMessageLocation compute; + DOCUMENT(R"(The location if the shader is a task or mesh shader. + +:type: ShaderMeshMessageLocation +)"); + ShaderMeshMessageLocation mesh; + DOCUMENT(R"(The location if the shader is a vertex shader. :type: ShaderVertexMessageLocation @@ -696,7 +764,7 @@ struct ShaderMessage bool operator==(const ShaderMessage &o) const { return stage == o.stage && disassemblyLine == o.disassemblyLine && - location.compute == o.location.compute && message == o.message; + location.mesh == o.location.mesh && message == o.message; } bool operator<(const ShaderMessage &o) const { @@ -704,8 +772,8 @@ struct ShaderMessage return stage < o.stage; if(!(disassemblyLine == o.disassemblyLine)) return disassemblyLine < o.disassemblyLine; - if(!(location.compute == o.location.compute)) - return location.compute < o.location.compute; + if(!(location.mesh == o.location.mesh)) + return location.mesh < o.location.mesh; if(!(message == o.message)) return message < o.message; return false; diff --git a/renderdoc/api/replay/control_types.h b/renderdoc/api/replay/control_types.h index f55f66093..1ddfff5f9 100644 --- a/renderdoc/api/replay/control_types.h +++ b/renderdoc/api/replay/control_types.h @@ -31,6 +31,59 @@ #include "rdcarray.h" #include "replay_enums.h" +DOCUMENT(R"(The size information for a task group. +)"); +struct TaskGroupSize +{ + DOCUMENT("The size in the x dimension."); + uint32_t x; + DOCUMENT("The size in the y dimension."); + uint32_t y; + DOCUMENT("The size in the z dimension."); + uint32_t z; + + bool operator==(const TaskGroupSize &o) const { return x == o.x && y == o.y && z == o.z; } + bool operator<(const TaskGroupSize &o) const + { + if(!(x == o.x)) + return x < o.x; + if(!(y == o.y)) + return y < o.y; + if(!(z == o.z)) + return z < o.z; + return false; + } +}; + +DECLARE_REFLECTION_STRUCT(TaskGroupSize); + +DOCUMENT(R"(The size information for a meshlet. +)"); +struct MeshletSize +{ + DOCUMENT("The number of indices in the meshlet."); + uint32_t numIndices; + DOCUMENT(R"(The number of vertices in this meshlet. This may be larger or smaller than the number +of indices. +)"); + uint32_t numVertices; + + bool operator==(const MeshletSize &o) const + { + return numIndices == o.numIndices && numVertices == o.numVertices; + } + bool operator<(const MeshletSize &o) const + { + if(!(numIndices == o.numIndices)) + return numIndices < o.numIndices; + if(!(numVertices == o.numVertices)) + return numVertices < o.numVertices; + return false; + } +}; + +DECLARE_REFLECTION_STRUCT(MeshletSize); + DOCUMENT(R"(Contains the details of a single element of data (such as position or texture co-ordinates) within a mesh. )"); @@ -60,6 +113,54 @@ struct MeshFormat DOCUMENT("The number of bytes to use from the vertex buffer. Only valid on APIs that allow it."); uint64_t vertexByteSize = 0; + DOCUMENT(R"(The size of each meshlet, for a meshlet based draw. + +Each meshlet lists its individual size, but a cumulative sum can be used for defining boundaries +between meshlets either by raw vertex order (using the number of indices) or by index value (using +the number of vertices). + +:type: List[MeshletSize] +)"); + rdcarray meshletSizes; + + DOCUMENT(R"(The size of each task group's dispatch, for a meshlet based draw. + +Each group of a task shader within a dispatch can itself fill out a payload and dispatch a number +of mesh groups. This list contains the 3-dimensional dimension that each task group emitted. + +:type: List[TaskGroupSize] +)"); + rdcarray taskSizes; + + DOCUMENT(R"(If showing a set of meshlets that don't start from meshlet 0, this is the number of +meshlet to consider skipped before :data:`meshletSizes`. + +Primarily useful for keeping a consistent colouring of meshlets when filtering to a subset + +See also :data:`meshletIndexOffset`. +)"); + uint32_t meshletOffset = 0; + + DOCUMENT(R"(If showing a set of meshlets that don't start from index 0, this is the number of +vertices to consider skipped before :data:`meshletSizes` - equivalent to baseVertex. + +Primarily useful for keeping a consistent colouring of meshlets when filtering to a subset + +See also :data:`meshletOffset`. +)"); + uint32_t meshletIndexOffset = 0; + + DOCUMENT(R"(The offset in bytes to the start of the per-primitive rate vertex data. + +Only for meshlet outputs. +)"); + uint64_t perPrimitiveOffset = 0; + DOCUMENT(R"(The stride in bytes of the per-primitive rate vertex data. + +Only for meshlet outputs. +)"); + uint32_t perPrimitiveStride = 0; + DOCUMENT(R"(The format description of this mesh components elements. :type: ResourceFormat @@ -130,7 +231,7 @@ struct MeshDisplay MeshDisplay &operator=(const MeshDisplay &) = default; DOCUMENT("The :class:`MeshDataStage` where this mesh data comes from."); - MeshDataStage type = MeshDataStage::Unknown; + MeshDataStage type = MeshDataStage::VSIn; DOCUMENT(R"(The camera to use when rendering all of the meshes. diff --git a/renderdoc/api/replay/d3d12_pipestate.h b/renderdoc/api/replay/d3d12_pipestate.h index cf808b7b9..7fa59c7fa 100644 --- a/renderdoc/api/replay/d3d12_pipestate.h +++ b/renderdoc/api/replay/d3d12_pipestate.h @@ -974,6 +974,16 @@ struct State :type: D3D12Shader )"); Shader computeShader; + DOCUMENT(R"(The amplification shader stage. + +:type: D3D12Shader +)"); + Shader ampShader; + DOCUMENT(R"(The mesh shader stage. + +:type: D3D12Shader +)"); + Shader meshShader; DOCUMENT(R"(The stream-out pipeline stage. diff --git a/renderdoc/api/replay/pipestate.h b/renderdoc/api/replay/pipestate.h index bbf574014..fcdaab815 100644 --- a/renderdoc/api/replay/pipestate.h +++ b/renderdoc/api/replay/pipestate.h @@ -442,4 +442,8 @@ private: const D3D12Pipe::Shader &GetD3D12Stage(ShaderStage stage) const; const GLPipe::Shader &GetGLStage(ShaderStage stage) const; const VKPipe::Shader &GetVulkanStage(ShaderStage stage) const; + bool IsD3D11Stage(ShaderStage stage) const; + bool IsD3D12Stage(ShaderStage stage) const; + bool IsGLStage(ShaderStage stage) const; + bool IsVulkanStage(ShaderStage stage) const; }; diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index a33f916ca..5a272c16d 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -62,6 +62,8 @@ rdcstr PipeState::Abbrev(ShaderStage stage) const case ShaderStage::Geometry: return "GS"; case ShaderStage::Fragment: return "FS"; case ShaderStage::Compute: return "CS"; + case ShaderStage::Task: return "TS"; + case ShaderStage::Mesh: return "MS"; default: break; } } @@ -75,6 +77,8 @@ rdcstr PipeState::Abbrev(ShaderStage stage) const case ShaderStage::Geometry: return "GS"; case ShaderStage::Pixel: return "PS"; case ShaderStage::Compute: return "CS"; + case ShaderStage::Amplification: return "AS"; + case ShaderStage::Mesh: return "MS"; default: break; } } @@ -92,6 +96,62 @@ rdcstr PipeState::OutputAbbrev() const return "RT"; } +bool PipeState::IsD3D11Stage(ShaderStage stage) const +{ + switch(stage) + { + case ShaderStage::Vertex: + case ShaderStage::Domain: + case ShaderStage::Hull: + case ShaderStage::Geometry: + case ShaderStage::Pixel: + case ShaderStage::Compute: return true; + default: return false; + } +} + +bool PipeState::IsD3D12Stage(ShaderStage stage) const +{ + switch(stage) + { + case ShaderStage::Vertex: + case ShaderStage::Domain: + case ShaderStage::Hull: + case ShaderStage::Geometry: + case ShaderStage::Pixel: + case ShaderStage::Compute: return true; + default: return false; + } +} + +bool PipeState::IsGLStage(ShaderStage stage) const +{ + switch(stage) + { + case ShaderStage::Vertex: + case ShaderStage::Domain: + case ShaderStage::Hull: + case ShaderStage::Geometry: + case ShaderStage::Pixel: + case ShaderStage::Compute: return true; + default: return false; + } +} + +bool PipeState::IsVulkanStage(ShaderStage stage) const +{ + switch(stage) + { + case ShaderStage::Vertex: + case ShaderStage::Domain: + case ShaderStage::Hull: + case ShaderStage::Geometry: + case ShaderStage::Pixel: + case ShaderStage::Compute: return true; + default: return false; + } +} + const D3D11Pipe::Shader &PipeState::GetD3D11Stage(ShaderStage stage) const { if(stage == ShaderStage::Vertex) @@ -952,6 +1012,9 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui { if(IsCaptureD3D11()) { + if(!IsD3D11Stage(stage)) + return ret; + const D3D11Pipe::Shader &s = GetD3D11Stage(stage); if(s.reflection != NULL && BufIdx < (uint32_t)s.reflection->constantBlocks.count()) @@ -971,6 +1034,9 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui } else if(IsCaptureD3D12()) { + if(!IsD3D12Stage(stage)) + return ret; + const D3D12Pipe::Shader &s = GetD3D12Stage(stage); if(s.reflection != NULL && BufIdx < (uint32_t)s.reflection->constantBlocks.count()) @@ -1014,6 +1080,9 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui } else if(IsCaptureGL()) { + if(!IsGLStage(stage)) + return ret; + const GLPipe::Shader &s = GetGLStage(stage); if(s.reflection != NULL && BufIdx < (uint32_t)s.reflection->constantBlocks.count()) @@ -1038,6 +1107,9 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui } else if(IsCaptureVK()) { + if(!IsVulkanStage(stage)) + return ret; + const VKPipe::Pipeline &pipe = stage == ShaderStage::Compute ? m_Vulkan->compute : m_Vulkan->graphics; const VKPipe::Shader &s = GetVulkanStage(stage); @@ -1150,6 +1222,9 @@ rdcarray PipeState::GetSamplers(ShaderStage stage) const { if(IsCaptureD3D11()) { + if(!IsD3D11Stage(stage)) + return ret; + const D3D11Pipe::Shader &s = GetD3D11Stage(stage); ret.reserve(s.samplers.size()); @@ -1168,6 +1243,9 @@ rdcarray PipeState::GetSamplers(ShaderStage stage) const } else if(IsCaptureD3D12()) { + if(!IsD3D12Stage(stage)) + return ret; + const D3D12Pipe::Shader &s = GetD3D12Stage(stage); size_t size = s.bindpointMapping.samplers.size(); @@ -1210,6 +1288,9 @@ rdcarray PipeState::GetSamplers(ShaderStage stage) const } else if(IsCaptureGL()) { + if(!IsGLStage(stage)) + return ret; + ret.reserve(m_GL->samplers.size()); for(int i = 0; i < m_GL->samplers.count(); i++) @@ -1226,6 +1307,9 @@ rdcarray PipeState::GetSamplers(ShaderStage stage) const } else if(IsCaptureVK()) { + if(!IsVulkanStage(stage)) + return ret; + const rdcarray &descsets = stage == ShaderStage::Compute ? m_Vulkan->compute.descriptorSets : m_Vulkan->graphics.descriptorSets; @@ -1282,6 +1366,9 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage, { if(IsCaptureD3D11()) { + if(!IsD3D11Stage(stage)) + return ret; + const D3D11Pipe::Shader &s = GetD3D11Stage(stage); ret.reserve(s.srvs.size()); @@ -1303,6 +1390,9 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage, } else if(IsCaptureD3D12()) { + if(!IsD3D12Stage(stage)) + return ret; + const D3D12Pipe::Shader &s = GetD3D12Stage(stage); size_t size = s.bindpointMapping.readOnlyResources.size(); @@ -1370,6 +1460,9 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage, } else if(IsCaptureGL()) { + if(!IsGLStage(stage)) + return ret; + ret.reserve(m_GL->textures.size()); for(int i = 0; i < m_GL->textures.count(); i++) @@ -1389,6 +1482,9 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage, } else if(IsCaptureVK()) { + if(!IsVulkanStage(stage)) + return ret; + const rdcarray &descsets = stage == ShaderStage::Compute ? m_Vulkan->compute.descriptorSets : m_Vulkan->graphics.descriptorSets; @@ -1478,6 +1574,9 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage, { if(IsCaptureD3D11()) { + if(!IsD3D11Stage(stage)) + return ret; + if(stage == ShaderStage::Compute) { ret.reserve(m_D3D11->computeShader.uavs.size()); @@ -1526,6 +1625,9 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage, } else if(IsCaptureD3D12()) { + if(!IsD3D12Stage(stage)) + return ret; + const D3D12Pipe::Shader &s = GetD3D12Stage(stage); size_t size = s.bindpointMapping.readWriteResources.size(); @@ -1591,6 +1693,9 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage, } else if(IsCaptureGL()) { + if(!IsGLStage(stage)) + return ret; + ret.reserve(m_GL->images.size() + m_GL->atomicBuffers.size() + m_GL->shaderStorageBuffers.size()); @@ -1629,6 +1734,9 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage, } else if(IsCaptureVK()) { + if(!IsVulkanStage(stage)) + return ret; + const rdcarray &descsets = stage == ShaderStage::Compute ? m_Vulkan->compute.descriptorSets : m_Vulkan->graphics.descriptorSets; diff --git a/renderdoc/api/replay/renderdoc_tostr.inl b/renderdoc/api/replay/renderdoc_tostr.inl index e5d690124..57f3fdb42 100644 --- a/renderdoc/api/replay/renderdoc_tostr.inl +++ b/renderdoc/api/replay/renderdoc_tostr.inl @@ -692,6 +692,7 @@ rdcstr DoStringise(const ShaderBuiltin &el) STRINGISE_ENUM_CLASS_NAMED(PackedFragRate, "Packed Fragment Rate"); STRINGISE_ENUM_CLASS_NAMED(Barycentrics, "Barycentrics"); STRINGISE_ENUM_CLASS_NAMED(CullPrimitive, "Cull Primitive Output"); + STRINGISE_ENUM_CLASS_NAMED(OutputIndices, "Output Indices"); } END_ENUM_STRINGISE(); } @@ -918,6 +919,8 @@ rdcstr DoStringise(const GPUCounter &el) STRINGISE_ENUM_CLASS(GSInvocations); STRINGISE_ENUM_CLASS(PSInvocations); STRINGISE_ENUM_CLASS(CSInvocations); + STRINGISE_ENUM_CLASS(TSInvocations); + STRINGISE_ENUM_CLASS(MSInvocations); } END_ENUM_STRINGISE(); } @@ -948,6 +951,8 @@ rdcstr DoStringise(const ShaderStage &el) STRINGISE_ENUM_CLASS(Geometry); STRINGISE_ENUM_CLASS(Pixel); STRINGISE_ENUM_CLASS(Compute); + STRINGISE_ENUM_CLASS(Task); + STRINGISE_ENUM_CLASS(Mesh); } END_ENUM_STRINGISE(); } @@ -957,10 +962,11 @@ rdcstr DoStringise(const MeshDataStage &el) { BEGIN_ENUM_STRINGISE(MeshDataStage) { - STRINGISE_ENUM_CLASS(Unknown); STRINGISE_ENUM_CLASS(VSIn); STRINGISE_ENUM_CLASS(VSOut); STRINGISE_ENUM_CLASS(GSOut); + STRINGISE_ENUM_CLASS(TaskOut); + STRINGISE_ENUM_CLASS(MeshOut); } END_ENUM_STRINGISE(); } @@ -1214,6 +1220,7 @@ rdcstr DoStringise(const ActionFlags &el) STRINGISE_BITFIELD_CLASS_BIT(Clear); STRINGISE_BITFIELD_CLASS_BIT(Drawcall); STRINGISE_BITFIELD_CLASS_BIT(Dispatch); + STRINGISE_BITFIELD_CLASS_BIT(MeshDispatch); STRINGISE_BITFIELD_CLASS_BIT(CmdList); STRINGISE_BITFIELD_CLASS_BIT(SetMarker); STRINGISE_BITFIELD_CLASS_BIT(PushMarker); @@ -1252,6 +1259,8 @@ rdcstr DoStringise(const ShaderStageMask &el) STRINGISE_BITFIELD_CLASS_BIT(Geometry); STRINGISE_BITFIELD_CLASS_BIT(Pixel); STRINGISE_BITFIELD_CLASS_BIT(Compute); + STRINGISE_BITFIELD_CLASS_BIT(Task); + STRINGISE_BITFIELD_CLASS_BIT(Mesh); } END_BITFIELD_STRINGISE(); } diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 7b705039b..f334613c7 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -831,7 +831,7 @@ enum class BindType : uint32_t DECLARE_REFLECTION_ENUM(BindType); -DOCUMENT2(R"(Annotates a particular built-in input or output from a shader with a special meaning to +DOCUMENT3(R"(Annotates a particular built-in input or output from a shader with a special meaning to the hardware or API. Some of the built-in inputs or outputs can be declared multiple times in arrays or otherwise indexed @@ -929,6 +929,8 @@ to apply to multiple related things - see :data:`ClipDistance`, :data:`CullDista This is related to :data:`GroupIndex` and :data:`DispatchThreadIndex`. +)", + R"( .. data:: GSInstanceIndex An input to the geometry shader giving the instance being run, if the geometry shader was setup to @@ -956,8 +958,6 @@ to apply to multiple related things - see :data:`ClipDistance`, :data:`CullDista in a pixel were covered by the rasterizer. As an output, it specifies which samples in the destination target should be updated. -)", - R"( .. data:: MSAASamplePosition An input to the pixel shader that contains the location of the current sample relative to the @@ -1034,6 +1034,8 @@ to apply to multiple related things - see :data:`ClipDistance`, :data:`CullDista Indicates if the current invocation is a helper invocation. +)", + R"( .. data:: SubgroupSize The number of invocations in a subgroup. @@ -1103,6 +1105,10 @@ to apply to multiple related things - see :data:`ClipDistance`, :data:`CullDista .. data:: CullPrimitive An output to indicate whether or not a primitive should be culled. + +.. data:: OutputIndices + + An output containing the indices for a meshlet. )"); enum class ShaderBuiltin : uint32_t { @@ -1159,6 +1165,7 @@ enum class ShaderBuiltin : uint32_t PackedFragRate, Barycentrics, CullPrimitive, + OutputIndices, Count, }; @@ -1191,10 +1198,6 @@ DECLARE_REFLECTION_ENUM(ReplayOutputType); DOCUMENT(R"(Describes a particular stage in the geometry transformation pipeline. -.. data:: Unknown - - Unknown or invalid stage. - .. data:: VSIn The inputs to the vertex shader described by the explicit API vertex input bindings. @@ -1208,13 +1211,29 @@ DOCUMENT(R"(Describes a particular stage in the geometry transformation pipeline The final output from the last stage in the pipeline, be that tessellation or geometry shader. This has possibly been expanded/multiplied from the inputs + +.. data:: TaskOut + + Data from a task/amplification shader. + +.. data:: AmpOut + + Data from an amplification shader (alias for :data:`TaskOut`). + +.. data:: MeshOut + + Data from a mesh shader. )"); enum class MeshDataStage : uint32_t { - Unknown = 0, - VSIn, + VSIn = 0, + First = VSIn, VSOut, GSOut, + TaskOut, + AmpOut = TaskOut, + MeshOut, + Count, }; DECLARE_REFLECTION_ENUM(MeshDataStage); @@ -2336,6 +2355,18 @@ DOCUMENT(R"(The stage in a pipeline where a shader runs .. data:: Compute The compute shader. + +.. data:: Amplification + + The amplification shader. See also :data:`Task`. + +.. data:: Task + + The task shader. See also :data:`Amplification`. + +.. data:: Mesh + + The mesh shader. )"); enum class ShaderStage : uint32_t { @@ -2355,12 +2386,19 @@ enum class ShaderStage : uint32_t Compute, + Task, + Amplification = Task, + + Mesh, + Count, }; ITERABLE_OPERATORS(ShaderStage); DECLARE_REFLECTION_ENUM(ShaderStage); +#define NumShaderStages arraydim() + template constexpr inline ShaderStage StageFromIndex(integer stage) { @@ -2528,7 +2566,7 @@ enum class MessageSource : uint32_t DECLARE_REFLECTION_ENUM(MessageSource); -DOCUMENT(R"(How a resource is being used in the pipeline at a particular point. +DOCUMENT2(R"(How a resource is being used in the pipeline at a particular point. Note that a resource may be used for more than one thing in one event, see :class:`EventUsage`. @@ -2570,6 +2608,15 @@ Note that a resource may be used for more than one thing in one event, see :clas The resource is being used for constants in the :data:`compute shader `. +.. data:: TS_Constants + + The resource is being used as a constants in the amplification or + :data:`task shader `. + +.. data:: MS_Constants + + The resource is being used as a constants in the :data:`mesh shader `. + .. data:: All_Constants The resource is being used for constants in all shader stages. @@ -2608,10 +2655,22 @@ Note that a resource may be used for more than one thing in one event, see :clas The resource is being used as a read-only resource in the :data:`compute shader `. +.. data:: TS_Resource + + The resource is being used as a read-only resource in the amplification or + :data:`task shader `. + +.. data:: MS_Resource + + The resource is being used as a read-only resource in the + :data:`mesh shader `. + .. data:: All_Resource The resource is being used as a read-only resource in all shader stages. +)", + R"( .. data:: VS_RWResource The resource is being used as a read-write resource in the @@ -2642,6 +2701,16 @@ Note that a resource may be used for more than one thing in one event, see :clas The resource is being used as a read-write resource in the :data:`compute shader `. +.. data:: TS_RWResource + + The resource is being used as a read-write resource in the amplification or + :data:`task shader `. + +.. data:: MS_RWResource + + The resource is being used as a read-write resource in the + :data:`mesh shader `. + .. data:: All_RWResource The resource is being used as a read-write resource in all shader stages. @@ -2720,6 +2789,8 @@ enum class ResourceUsage : uint32_t GS_Constants, PS_Constants, CS_Constants, + TS_Constants, + MS_Constants, All_Constants, @@ -2731,6 +2802,8 @@ enum class ResourceUsage : uint32_t GS_Resource, PS_Resource, CS_Resource, + TS_Resource, + MS_Resource, All_Resource, @@ -2740,6 +2813,8 @@ enum class ResourceUsage : uint32_t GS_RWResource, PS_RWResource, CS_RWResource, + TS_RWResource, + MS_RWResource, All_RWResource, @@ -2838,6 +2913,10 @@ DOCUMENT(R"(What kind of solid shading to use when rendering a mesh. The mesh should be rendered using the secondary element as color. +.. data:: Meshlet + + The mesh should be rendered colorising each meshlet differently. + )"); enum class SolidShade : uint32_t { @@ -2845,6 +2924,7 @@ enum class SolidShade : uint32_t Solid, Lit, Secondary, + Meshlet, Count, }; @@ -3495,6 +3575,18 @@ enumerated with IDs in the appropriate ranges. Number of times a :data:`compute shader ` was invoked. +.. data:: TSInvocations + + Number of times a :data:`task shader ` was invoked. + +.. data:: ASInvocations + + Number of times a :data:`amplification shader ` was invoked. + +.. data:: MSInvocations + + Number of times a :data:`mesh shader ` was invoked. + .. data:: FirstAMD The AMD-specific counter IDs start from this value. @@ -3554,6 +3646,9 @@ enum class GPUCounter : uint32_t PSInvocations, FSInvocations = PSInvocations, CSInvocations, + ASInvocations, + TSInvocations = ASInvocations, + MSInvocations, Count, // IHV specific counters can be set above this point @@ -4416,7 +4511,10 @@ enum class ShaderStageMask : uint32_t Pixel = 1 << uint32_t(ShaderStage::Pixel), Fragment = Pixel, Compute = 1 << uint32_t(ShaderStage::Compute), - All = Vertex | Hull | Domain | Geometry | Pixel | Compute, + Task = 1 << uint32_t(ShaderStage::Task), + Amplification = Task, + Mesh = 1 << uint32_t(ShaderStage::Mesh), + All = Vertex | Hull | Domain | Geometry | Pixel | Compute | Task | Mesh, }; BITMASK_OPERATORS(ShaderStageMask); @@ -4541,6 +4639,10 @@ actions. The action issues a number of compute workgroups. +.. data:: MeshDispatch + + The action issues a number of mesh groups for a draw. + .. data:: CmdList The action calls into a previously recorded child command list. @@ -4629,16 +4731,17 @@ enum class ActionFlags : uint32_t Clear = 0x0001, Drawcall = 0x0002, Dispatch = 0x0004, - CmdList = 0x0008, - SetMarker = 0x0010, - PushMarker = 0x0020, - PopMarker = 0x0040, // this is only for internal tracking use - Present = 0x0080, - MultiAction = 0x0100, - Copy = 0x0200, - Resolve = 0x0400, - GenMips = 0x0800, - PassBoundary = 0x1000, + MeshDispatch = 0x0008, + CmdList = 0x0010, + SetMarker = 0x0020, + PushMarker = 0x0040, + PopMarker = 0x0080, + Present = 0x0100, + MultiAction = 0x0200, + Copy = 0x0400, + Resolve = 0x0800, + GenMips = 0x1000, + PassBoundary = 0x2000, // flags Indexed = 0x010000, diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 5427d1b3e..1bafe8cdd 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -930,7 +930,12 @@ struct SigParameter DOCUMENT("The combined semantic name and index."); rdcstr semanticIdxName; DOCUMENT("The semantic index of this variable - see :data:`semanticName`."); - uint32_t semanticIndex = 0; + uint16_t semanticIndex = 0; + + DOCUMENT( + "A flag indicating if this parameter is output at per-primitive rate rather than " + "per-vertex."); + bool perPrimitiveRate = false; DOCUMENT(R"(The index of the shader register/binding used to store this signature element. @@ -956,7 +961,7 @@ shader itself, for APIs that pack signatures together. DOCUMENT("A convenience flag - ``True`` if the semantic name is unique and no index is needed."); bool needSemanticIndex = false; - DOCUMENT("The number of components used to store this element. See :data:`compType`."); + DOCUMENT("The number of components used to store this element. See :data:`varType`."); uint32_t compCount = 0; DOCUMENT( "Selects a stream for APIs that provide multiple output streams for the same named output."); @@ -1526,6 +1531,12 @@ struct ShaderReflection )"); rdcfixedarray dispatchThreadsDimension; + DOCUMENT(R"(The output topology for geometry, tessellation and mesh shaders. + +:type: Topology +)"); + Topology outputTopology = Topology::Unknown; + DOCUMENT(R"(The input signature. :type: List[SigParameter] @@ -1574,6 +1585,15 @@ struct ShaderReflection :type: List[ShaderConstantType] )"); rdcarray pointerTypes; + + DOCUMENT(R"(The block layout of the task-mesh communication payload. + +Only relevant for task or mesh shaders, this gives the output payload (for task shaders) or the +input payload (for mesh shaders) + +:type: ConstantBlock +)"); + ConstantBlock taskPayload; }; DECLARE_REFLECTION_STRUCT(ShaderReflection); diff --git a/renderdoc/api/replay/vk_pipestate.h b/renderdoc/api/replay/vk_pipestate.h index 0c311eff5..ea77829c5 100644 --- a/renderdoc/api/replay/vk_pipestate.h +++ b/renderdoc/api/replay/vk_pipestate.h @@ -1394,6 +1394,18 @@ struct State )"); Shader computeShader; + DOCUMENT(R"(The task shader stage. + +:type: VKShader +)"); + Shader taskShader; + + DOCUMENT(R"(The mesh shader stage. + +:type: VKShader +)"); + Shader meshShader; + DOCUMENT(R"(The tessellation stage. :type: VKTessellation diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index c50197c1d..9e54f60ba 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -1762,7 +1762,7 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer &m_D3D11PipelineState->pixelShader, &m_D3D11PipelineState->computeShader, }; - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < ARRAY_COUNT(stages); i++) if(stages[i]->resourceId != ResourceId()) stages[i]->reflection = GetShader(ResourceId(), GetLiveID(stages[i]->resourceId), ShaderEntryPoint()); @@ -1778,11 +1778,12 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer &m_D3D12PipelineState->vertexShader, &m_D3D12PipelineState->hullShader, &m_D3D12PipelineState->domainShader, &m_D3D12PipelineState->geometryShader, &m_D3D12PipelineState->pixelShader, &m_D3D12PipelineState->computeShader, + &m_D3D12PipelineState->ampShader, &m_D3D12PipelineState->meshShader, }; ResourceId pipe = GetLiveID(m_D3D12PipelineState->pipelineResourceId); - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < ARRAY_COUNT(stages); i++) if(stages[i]->resourceId != ResourceId()) stages[i]->reflection = GetShader(pipe, GetLiveID(stages[i]->resourceId), ShaderEntryPoint()); @@ -1795,7 +1796,7 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer &m_GLPipelineState->fragmentShader, &m_GLPipelineState->computeShader, }; - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < ARRAY_COUNT(stages); i++) if(stages[i]->shaderResourceId != ResourceId()) stages[i]->reflection = GetShader(ResourceId(), GetLiveID(stages[i]->shaderResourceId), ShaderEntryPoint()); @@ -1806,11 +1807,12 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer &m_VulkanPipelineState->vertexShader, &m_VulkanPipelineState->tessControlShader, &m_VulkanPipelineState->tessEvalShader, &m_VulkanPipelineState->geometryShader, &m_VulkanPipelineState->fragmentShader, &m_VulkanPipelineState->computeShader, + &m_VulkanPipelineState->taskShader, &m_VulkanPipelineState->meshShader, }; ResourceId pipe = GetLiveID(m_VulkanPipelineState->graphics.pipelineResourceId); - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < ARRAY_COUNT(stages); i++) { if(i == 5) pipe = GetLiveID(m_VulkanPipelineState->compute.pipelineResourceId); @@ -2930,7 +2932,7 @@ bool ReplayProxy::Tick(int type) InitPostVSBuffers(dummy); break; } - case eReplayProxy_GetPostVS: GetPostVSBuffers(0, 0, 0, MeshDataStage::Unknown); break; + case eReplayProxy_GetPostVS: GetPostVSBuffers(0, 0, 0, MeshDataStage::VSIn); break; case eReplayProxy_BuildTargetShader: { rdcstr entry; diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index 314fb0728..c364d6834 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -1036,7 +1036,7 @@ void WrappedID3D11DeviceContext::AddUsage(const ActionDescription &a) ////////////////////////////// // Shaders - const D3D11RenderState::Shader *shArr[6] = { + const D3D11RenderState::Shader *shArr[NumShaderStages] = { &pipe->VS, &pipe->HS, &pipe->DS, &pipe->GS, &pipe->PS, &pipe->CS, }; diff --git a/renderdoc/driver/d3d11/d3d11_renderstate.cpp b/renderdoc/driver/d3d11/d3d11_renderstate.cpp index c3208078f..2fb280004 100644 --- a/renderdoc/driver/d3d11/d3d11_renderstate.cpp +++ b/renderdoc/driver/d3d11/d3d11_renderstate.cpp @@ -107,7 +107,7 @@ void D3D11RenderState::ReleaseRefs() IntRelease(IA.VBs[i]); Shader *stages[] = {&VS, &HS, &DS, &GS, &PS, &CS}; - for(int s = 0; s < 6; s++) + for(int s = 0; s < ARRAY_COUNT(stages); s++) { Shader *sh = stages[s]; @@ -176,7 +176,7 @@ void D3D11RenderState::MarkReferenced(WrappedID3D11DeviceContext *ctx, bool init initial ? eFrameRef_None : eFrameRef_Read); const Shader *stages[] = {&VS, &HS, &DS, &GS, &PS, &CS}; - for(int s = 0; s < 6; s++) + for(int s = 0; s < ARRAY_COUNT(stages); s++) { const Shader *sh = stages[s]; @@ -285,7 +285,7 @@ void D3D11RenderState::AddRefs() IntAddRef(IA.VBs[i]); Shader *stages[] = {&VS, &HS, &DS, &GS, &PS, &CS}; - for(int s = 0; s < 6; s++) + for(int s = 0; s < ARRAY_COUNT(stages); s++) { Shader *sh = stages[s]; @@ -606,7 +606,7 @@ void D3D11RenderState::UnbindRangeForRead(const ResourceRange &range) // const char *names[] = { "VS", "DS", "HS", "GS", "PS", "CS" }; Shader *stages[] = {&VS, &HS, &DS, &GS, &PS, &CS}; - for(int s = 0; s < 6; s++) + for(int s = 0; s < ARRAY_COUNT(stages); s++) { Shader *sh = stages[s]; diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index ad547c87b..ac7c039d9 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -802,7 +802,7 @@ void D3D11Replay::SavePipelineState(uint32_t eventId) const D3D11RenderState::Shader *srcArr[] = {&rs->VS, &rs->HS, &rs->DS, &rs->GS, &rs->PS, &rs->CS}; - for(size_t stage = 0; stage < 6; stage++) + for(size_t stage = 0; stage < ARRAY_COUNT(dstArr); stage++) { D3D11Pipe::Shader &dst = *dstArr[stage]; const D3D11RenderState::Shader &src = *srcArr[stage]; diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index c50056cc0..a8bdf31c2 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -2089,7 +2089,7 @@ void D3D12CommandData::AddUsage(const D3D12RenderState &state, D3D12ActionTreeNo if(state.pipe != ResourceId()) pipe = rm->GetCurrentAs(state.pipe); - const ShaderBindpointMapping *bindMap[6] = {}; + const ShaderBindpointMapping *bindMap[NumShaderStages] = {}; if((a.flags & ActionFlags::Dispatch) && state.compute.rootsig != ResourceId()) { diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index 285531b18..cc5812dcf 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -67,8 +67,11 @@ struct QueueReadbackData ID3D12Resource *readbackBuf = NULL; byte *readbackMapped = NULL; uint64_t readbackSize = 0; - ID3D12GraphicsCommandList *lists[6] = {}; - ID3D12CommandAllocator *allocs[6] = {}; + + static const uint32_t NumCommandTypes = 7; + + ID3D12GraphicsCommandList *lists[NumCommandTypes] = {}; + ID3D12CommandAllocator *allocs[NumCommandTypes] = {}; void Resize(uint64_t size); diff --git a/renderdoc/driver/d3d12/d3d12_postvs.cpp b/renderdoc/driver/d3d12/d3d12_postvs.cpp index a7b115fd8..2c19fcd6b 100644 --- a/renderdoc/driver/d3d12/d3d12_postvs.cpp +++ b/renderdoc/driver/d3d12/d3d12_postvs.cpp @@ -220,7 +220,6 @@ void D3D12Replay::InitPostVSBuffers(uint32_t eventId) D3D_PRIMITIVE_TOPOLOGY topo = rs.topo; - ret.vsin.topo = topo; ret.vsout.topo = topo; const ActionDescription *action = m_pDevice->GetAction(eventId); @@ -789,7 +788,6 @@ void D3D12Replay::InitPostVSBuffers(uint32_t eventId) m_SOStagingBuffer->Unmap(0, &range); - ret.vsin.topo = topo; ret.vsout.buf = vsoutBuffer; ret.vsout.vertStride = stride; ret.vsout.nearPlane = nearp; @@ -816,7 +814,6 @@ void D3D12Replay::InitPostVSBuffers(uint32_t eventId) else { // empty vertex output signature - ret.vsin.topo = topo; ret.vsout.buf = NULL; ret.vsout.instStride = 0; ret.vsout.vertStride = 0; diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 174c28828..347e803dd 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -333,7 +333,7 @@ private: float farPlane = 0.0f; rdcstr status; - } vsin, vsout, gsout; + } vsout, gsout, ampout, meshout; const StageData &GetStage(MeshDataStage type) { @@ -341,10 +341,14 @@ private: return vsout; else if(type == MeshDataStage::GSOut) return gsout; + else if(type == MeshDataStage::TaskOut) + return ampout; + else if(type == MeshDataStage::MeshOut) + return meshout; else RDCERR("Unexpected mesh data stage!"); - return vsin; + return vsout; } }; diff --git a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp index d8c51f3dc..7c9fe9ea6 100644 --- a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp +++ b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp @@ -1153,7 +1153,7 @@ bool D3D12Replay::FetchShaderFeedback(uint32_t eventId) m_pDevice->GetShaderCache()->LoadDXC(); #endif - bool dynamicAccessPerStage[6] = {}; + bool dynamicAccessPerStage[NumShaderStages] = {}; if(result.compute) { diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index c500d669b..c87abc341 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -902,8 +902,8 @@ struct ShaderReflection; struct PerStageReflections { - const ShaderReflection *refls[6] = {}; - const ShaderBindpointMapping *mappings[6] = {}; + const ShaderReflection *refls[NumShaderStages] = {}; + const ShaderBindpointMapping *mappings[NumShaderStages] = {}; }; void CopyProgramUniforms(const PerStageReflections &srcStages, GLuint progSrc, diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index f8ad55105..4077248c6 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -1818,7 +1818,7 @@ void WrappedOpenGL::RefreshDerivedReplacements() bool usesReplacedShader = false; - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) { if(GetResourceManager()->HasReplacement( GetResourceManager()->GetOriginalID(progdata.stageShaders[i]))) @@ -1839,7 +1839,7 @@ void WrappedOpenGL::RefreshDerivedReplacements() ResourceId progdstid = GetResourceManager()->GetResID(ProgramRes(GetCtx(), progdst)); // attach shaders, going via the original ID to pick up replacements - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) { if(progdata.stageShaders[i] != ResourceId()) { @@ -1947,7 +1947,7 @@ void WrappedOpenGL::RefreshDerivedReplacements() bool usesReplacedProgram = false; - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) { if(GetResourceManager()->HasReplacement( GetResourceManager()->GetOriginalID(pipedata.stagePrograms[i]))) @@ -1967,7 +1967,7 @@ void WrappedOpenGL::RefreshDerivedReplacements() ResourceId pipedstid = GetResourceManager()->GetResID(ProgramPipeRes(GetCtx(), pipedst)); // attach programs, going via the original ID to pick up replacements - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) { if(pipedata.stagePrograms[i] != ResourceId()) { @@ -5436,8 +5436,8 @@ void WrappedOpenGL::AddUsage(const ActionDescription &a) GLRenderState rs; rs.FetchState(this); - ShaderReflection *refl[6] = {NULL}; - ShaderBindpointMapping mapping[6]; + ShaderReflection *refl[NumShaderStages] = {NULL}; + ShaderBindpointMapping mapping[NumShaderStages]; GLuint curProg = 0; GL.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&curProg); diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 7971ad277..cde39ec8a 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -757,7 +757,7 @@ public: // the application to modify anything. bool shaderProgramUnlinkable = false; bool linked; - ResourceId stageShaders[6]; + ResourceId stageShaders[NumShaderStages]; // used only when we're capturing and don't have driver-side reflection so we need to emulate glslang::TProgram *glslangProgram = NULL; @@ -778,8 +778,8 @@ public: GLbitfield use; }; - ResourceId stagePrograms[6]; - ResourceId stageShaders[6]; + ResourceId stagePrograms[NumShaderStages]; + ResourceId stageShaders[NumShaderStages]; }; std::map m_Shaders; diff --git a/renderdoc/driver/gl/gl_initstate.h b/renderdoc/driver/gl/gl_initstate.h index 2062c6c90..5ad76a1c9 100644 --- a/renderdoc/driver/gl/gl_initstate.h +++ b/renderdoc/driver/gl/gl_initstate.h @@ -117,6 +117,8 @@ struct PipelineInitialData { bool valid; GLResource programs[6]; + // since this array is serialised (and because GL will never support other shader stages) we leave + // this at 6 instead of NumShaderStages }; DECLARE_REFLECTION_STRUCT(PipelineInitialData); diff --git a/renderdoc/driver/gl/gl_pixelhistory.cpp b/renderdoc/driver/gl/gl_pixelhistory.cpp index 7b9881d35..aa4f434bb 100644 --- a/renderdoc/driver/gl/gl_pixelhistory.cpp +++ b/renderdoc/driver/gl/gl_pixelhistory.cpp @@ -707,14 +707,15 @@ void CopyMSSample(WrappedOpenGL *driver, const GLPixelHistoryResources &resource driver->glUniform1i(depthMSLoc, 0); driver->glUniform1i(stencilMSLoc, 1); - uint32_t newUniforms[6] = {uint32_t(sampleIdx), - uint32_t(x), - uint32_t(y), - 1, - copyFramebuffer.dsTextureId != 0 || copyFramebuffer.depthTextureId != 0, - copyFramebuffer.dsTextureId != 0 || - copyFramebuffer.stencilTextureId != - 0}; // { sampleIdx, x, y, dstOffset, hasDepth, hasStencil } + // { sampleIdx, x, y, dstOffset, hasDepth, hasStencil } + uint32_t newUniforms[6] = { + uint32_t(sampleIdx), + uint32_t(x), + uint32_t(y), + 1, + copyFramebuffer.dsTextureId != 0 || copyFramebuffer.depthTextureId != 0, + copyFramebuffer.dsTextureId != 0 || copyFramebuffer.stencilTextureId != 0, + }; driver->glNamedBufferSubDataEXT(resources.msCopyUniformBlockBuffer, 0, sizeof(newUniforms), newUniforms); diff --git a/renderdoc/driver/gl/gl_program_iterate.cpp b/renderdoc/driver/gl/gl_program_iterate.cpp index b623d7881..dff728a6c 100644 --- a/renderdoc/driver/gl/gl_program_iterate.cpp +++ b/renderdoc/driver/gl/gl_program_iterate.cpp @@ -291,7 +291,7 @@ static void UnrollConstant(rdcarray &unrolled, const Shad static void UnrollConstants(const PerStageReflections &stages, rdcarray &globals) { - for(size_t s = 0; s < 6; s++) + for(size_t s = 0; s < NumShaderStages; s++) { if(!stages.refls[s]) continue; @@ -569,12 +569,12 @@ static void ForAllProgramUniforms(SerialiserType *ser, CaptureState state, // we only need to process uniform values - for compatibility we still serialise the same, but we // skip fetching or applying UBO bindings etc. bool IsSrcProgramSPIRV = false; - for(size_t i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) IsSrcProgramSPIRV |= srcStages.refls[i] && srcStages.refls[i]->encoding == ShaderEncoding::OpenGLSPIRV; bool IsDstProgramSPIRV = false; - for(size_t i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) IsDstProgramSPIRV |= dstStages.refls[i] && dstStages.refls[i]->encoding == ShaderEncoding::OpenGLSPIRV; @@ -1268,7 +1268,7 @@ bool SerialiseProgramBindings(SerialiserType &ser, CaptureState state, // instead just skip the fetch & apply steps, so that we can still serialise in a backwards // compatible way. bool IsProgramSPIRV = false; - for(size_t i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) IsProgramSPIRV |= stages.refls[i] && stages.refls[i]->encoding == ShaderEncoding::OpenGLSPIRV; const bool hasVert = stages.refls[0] != NULL; diff --git a/renderdoc/driver/gl/gl_renderstate.h b/renderdoc/driver/gl/gl_renderstate.h index c99c206d0..2d011288d 100644 --- a/renderdoc/driver/gl/gl_renderstate.h +++ b/renderdoc/driver/gl/gl_renderstate.h @@ -157,6 +157,8 @@ struct GLRenderState GLint numSubroutines; GLuint Values[128]; } Subroutines[6]; + // since this array is serialised (and because GL will never support other shader stages) we leave + // this at 6 instead of NumShaderStages enum { diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 51f28ae99..1921d361e 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -1054,16 +1054,19 @@ void GLReplay::SavePipelineState(uint32_t eventId) GLuint curProg = 0; drv.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&curProg); - GLPipe::Shader *stages[6] = { + GLPipe::Shader *stages[NumShaderStages] = { &pipe.vertexShader, &pipe.tessControlShader, &pipe.tessEvalShader, &pipe.geometryShader, &pipe.fragmentShader, &pipe.computeShader, }; - ShaderReflection *refls[6] = {NULL}; - ShaderBindpointMapping *mappings[6] = {NULL}; - bool spirv[6] = {false}; + ShaderReflection *refls[NumShaderStages] = {NULL}; + ShaderBindpointMapping *mappings[NumShaderStages] = {NULL}; + bool spirv[NumShaderStages] = {false}; - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < NumShaderStages; i++) { + if(!stages[i]) + continue; + stages[i]->programResourceId = stages[i]->shaderResourceId = ResourceId(); stages[i]->reflection = NULL; stages[i]->bindpointMapping = ShaderBindpointMapping(); @@ -1090,6 +1093,9 @@ void GLReplay::SavePipelineState(uint32_t eventId) for(size_t i = 0; i < ARRAY_COUNT(pipeDetails.stageShaders); i++) { + if(!stages[i]) + continue; + if(pipeDetails.stageShaders[i] != ResourceId()) { curProg = rm->GetCurrentResource(pipeDetails.stagePrograms[i]).name; @@ -1134,6 +1140,9 @@ void GLReplay::SavePipelineState(uint32_t eventId) for(size_t i = 0; i < ARRAY_COUNT(progDetails.stageShaders); i++) { + if(!stages[i]) + continue; + if(progDetails.stageShaders[i] != ResourceId()) { auto &shaderDetails = m_pDriver->m_Shaders[progDetails.stageShaders[i]]; @@ -1214,7 +1223,7 @@ void GLReplay::SavePipelineState(uint32_t eventId) pipe.transformFeedback.active = (p != 0) || m_pDriver->m_WasActiveFeedback; } - for(int i = 0; i < 6; i++) + for(size_t i = 0; i < ARRAY_COUNT(rs.Subroutines); i++) { size_t num = RDCMIN(128, rs.Subroutines[i].numSubroutines); if(num == 0) diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index a142aebea..0b54932ee 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -842,7 +842,7 @@ bool WrappedOpenGL::Serialise_glLinkProgram(SerialiserType &ser, GLuint programH progDetails.linked = true; - for(size_t s = 0; s < 6; s++) + for(size_t s = 0; s < NumShaderStages; s++) { for(size_t sh = 0; sh < progDetails.shaders.size(); sh++) { @@ -916,7 +916,7 @@ void WrappedOpenGL::glLinkProgram(GLuint program) progDetails.linked = true; - for(size_t s = 0; s < 6; s++) + for(size_t s = 0; s < NumShaderStages; s++) { for(size_t sh = 0; sh < progDetails.shaders.size(); sh++) { @@ -1494,7 +1494,7 @@ bool WrappedOpenGL::Serialise_glUseProgramStages(SerialiserType &ser, GLuint pip PipelineData &pipeDetails = m_Pipelines[livePipeId]; ProgramData &progDetails = m_Programs[liveProgId]; - for(size_t s = 0; s < 6; s++) + for(size_t s = 0; s < NumShaderStages; s++) { if(stages & ShaderBit(s)) { @@ -1517,7 +1517,7 @@ bool WrappedOpenGL::Serialise_glUseProgramStages(SerialiserType &ser, GLuint pip ResourceId livePipeId = GetResourceManager()->GetResID(pipeline); PipelineData &pipeDetails = m_Pipelines[livePipeId]; - for(size_t s = 0; s < 6; s++) + for(size_t s = 0; s < NumShaderStages; s++) { if(stages & ShaderBit(s)) { @@ -1593,7 +1593,7 @@ void WrappedOpenGL::glUseProgramStages(GLuint pipeline, GLbitfield stages, GLuin PipelineData &pipeDetails = m_Pipelines[pipeID]; ProgramData &progDetails = m_Programs[progID]; - for(size_t s = 0; s < 6; s++) + for(size_t s = 0; s < NumShaderStages; s++) { if(stages & ShaderBit(s)) { @@ -1614,7 +1614,7 @@ void WrappedOpenGL::glUseProgramStages(GLuint pipeline, GLbitfield stages, GLuin ResourceId pipeID = GetResourceManager()->GetResID(ProgramPipeRes(GetCtx(), pipeline)); PipelineData &pipeDetails = m_Pipelines[pipeID]; - for(size_t s = 0; s < 6; s++) + for(size_t s = 0; s < NumShaderStages; s++) { if(stages & ShaderBit(s)) { diff --git a/renderdoc/driver/ihv/amd/amd_isa.cpp b/renderdoc/driver/ihv/amd/amd_isa.cpp index 7a2731114..2cbd5a8f9 100644 --- a/renderdoc/driver/ihv/amd/amd_isa.cpp +++ b/renderdoc/driver/ihv/amd/amd_isa.cpp @@ -296,6 +296,8 @@ rdcstr DisassembleSPIRV(ShaderStage stage, const bytebuf &shaderBytes, const rdc case ShaderStage::Geometry: stageName = "geom"; break; case ShaderStage::Fragment: stageName = "frag"; break; case ShaderStage::Compute: stageName = "comp"; break; + case ShaderStage::Task: stageName = "task"; break; + case ShaderStage::Mesh: stageName = "mesh"; break; case ShaderStage::Count: return "; Cannot identify shader type"; } @@ -400,6 +402,14 @@ rdcstr DisassembleGLSL(ShaderStage stage, const bytebuf &shaderBytes, const rdcs stageIndex = 5; stageName = "comp"; break; + case ShaderStage::Task: + stageIndex = 6; + stageName = "task"; + break; + case ShaderStage::Mesh: + stageIndex = 7; + stageName = "mesh"; + break; case ShaderStage::Count: return "; Cannot identify shader type"; } diff --git a/renderdoc/driver/ihv/nv/nv_vk_counters.cpp b/renderdoc/driver/ihv/nv/nv_vk_counters.cpp index 62881cb36..1e6ebfc52 100644 --- a/renderdoc/driver/ihv/nv/nv_vk_counters.cpp +++ b/renderdoc/driver/ihv/nv/nv_vk_counters.cpp @@ -148,8 +148,9 @@ struct NVVulkanCounters::Impl if(actionnode.events.empty()) return false; // Skip nodes with no events - if(!(actionnode.flags & (ActionFlags::Clear | ActionFlags::Drawcall | ActionFlags::Dispatch | - ActionFlags::Present | ActionFlags::Copy | ActionFlags::Resolve))) + if(!(actionnode.flags & + (ActionFlags::Clear | ActionFlags::MeshDispatch | ActionFlags::Drawcall | + ActionFlags::Dispatch | ActionFlags::Present | ActionFlags::Copy | ActionFlags::Resolve))) return false; // Filter out events we cannot profile return true; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index d221ef32d..1f95edaae 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -1785,7 +1785,7 @@ DXBCContainer::DXBCContainer(const bytebuf &ByteCode, const rdcstr &debugInfoPat desc.regChannelMask = (uint8_t)(el->mask & 0xff); desc.channelUsedMask = (uint8_t)(el->rwMask & 0xff); desc.regIndex = el->registerNum; - desc.semanticIndex = el->semanticIdx; + desc.semanticIndex = (uint16_t)el->semanticIdx; desc.semanticName = chunkContents + el->nameOffset; desc.systemValue = GetSystemValue(el->systemType); desc.compCount = (desc.regChannelMask & 0x1 ? 1 : 0) + (desc.regChannelMask & 0x2 ? 1 : 0) + diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index 38796f881..6a700451e 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -5127,7 +5127,8 @@ void GatherPSInputDataForInitialValues(const DXBC::DXBCContainer *dxbc, } if(arrayLength > 0) - arrays.push_back(make_rdcpair(sig.semanticName, make_rdcpair(sig.semanticIndex, nextIdx - 1))); + arrays.push_back( + make_rdcpair(sig.semanticName, make_rdcpair((uint32_t)sig.semanticIndex, nextIdx - 1))); } if(included) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp b/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp index eee63555d..99b0ac911 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp @@ -277,6 +277,8 @@ void MakeShaderReflection(DXBC::DXBCContainer *dxbc, ShaderReflection *refl, case DXBC::ShaderType::Hull: refl->stage = ShaderStage::Hull; break; case DXBC::ShaderType::Domain: refl->stage = ShaderStage::Domain; break; case DXBC::ShaderType::Compute: refl->stage = ShaderStage::Compute; break; + case DXBC::ShaderType::Amplification: refl->stage = ShaderStage::Amplification; break; + case DXBC::ShaderType::Mesh: refl->stage = ShaderStage::Mesh; break; default: RDCERR("Unexpected DXBC shader type %u", dxbc->m_Type); refl->stage = ShaderStage::Vertex; diff --git a/renderdoc/driver/shaders/spirv/spirv_common.cpp b/renderdoc/driver/shaders/spirv/spirv_common.cpp index 922fca925..77a50e063 100644 --- a/renderdoc/driver/shaders/spirv/spirv_common.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_common.cpp @@ -100,9 +100,9 @@ ShaderStage MakeShaderStage(rdcspv::ExecutionModel model) case rdcspv::ExecutionModel::Geometry: return ShaderStage::Geometry; case rdcspv::ExecutionModel::Fragment: return ShaderStage::Fragment; case rdcspv::ExecutionModel::GLCompute: return ShaderStage::Compute; + case rdcspv::ExecutionModel::TaskEXT: return ShaderStage::Task; + case rdcspv::ExecutionModel::MeshEXT: return ShaderStage::Mesh; case rdcspv::ExecutionModel::Kernel: - case rdcspv::ExecutionModel::TaskEXT: - case rdcspv::ExecutionModel::MeshEXT: case rdcspv::ExecutionModel::RayGenerationKHR: case rdcspv::ExecutionModel::IntersectionKHR: case rdcspv::ExecutionModel::AnyHitKHR: @@ -181,6 +181,10 @@ ShaderBuiltin MakeShaderBuiltin(ShaderStage stage, const rdcspv::BuiltIn el) case rdcspv::BuiltIn::BaryCoordKHR: return ShaderBuiltin::Barycentrics; case rdcspv::BuiltIn::FragSizeEXT: return ShaderBuiltin::FragAreaSize; case rdcspv::BuiltIn::FragInvocationCountEXT: return ShaderBuiltin::FragInvocationCount; + case rdcspv::BuiltIn::PrimitivePointIndicesEXT: return ShaderBuiltin::OutputIndices; + case rdcspv::BuiltIn::PrimitiveLineIndicesEXT: return ShaderBuiltin::OutputIndices; + case rdcspv::BuiltIn::PrimitiveTriangleIndicesEXT: return ShaderBuiltin::OutputIndices; + case rdcspv::BuiltIn::CullPrimitiveEXT: return ShaderBuiltin::CullPrimitive; default: break; } diff --git a/renderdoc/driver/shaders/spirv/spirv_compile.cpp b/renderdoc/driver/shaders/spirv/spirv_compile.cpp index 16c47c831..8319db76d 100644 --- a/renderdoc/driver/shaders/spirv/spirv_compile.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_compile.cpp @@ -59,7 +59,9 @@ rdcstr rdcspv::Compile(const rdcspv::CompilationSettings &settings, const rdcarr (int)EShLangTessControl == (int)rdcspv::ShaderStage::TessControl && (int)EShLangTessEvaluation == (int)rdcspv::ShaderStage::TessEvaluation && (int)EShLangGeometry == (int)rdcspv::ShaderStage::Geometry && - (int)EShLangCompute == (int)rdcspv::ShaderStage::Compute, + (int)EShLangCompute == (int)rdcspv::ShaderStage::Compute && + (int)EShLangTask == (int)rdcspv::ShaderStage::Task && + (int)EShLangMesh == (int)rdcspv::ShaderStage::Mesh, "Shader language enums don't match"); { diff --git a/renderdoc/driver/shaders/spirv/spirv_compile.h b/renderdoc/driver/shaders/spirv/spirv_compile.h index ebbdeebc0..c5c0d253e 100644 --- a/renderdoc/driver/shaders/spirv/spirv_compile.h +++ b/renderdoc/driver/shaders/spirv/spirv_compile.h @@ -37,6 +37,9 @@ enum class ShaderStage Geometry, Fragment, Compute, + // gap of 6 for RT shaders + Task = Compute + 7, + Mesh, Invalid, }; diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp index bb06e024f..21b76b071 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp @@ -278,8 +278,14 @@ static int32_t GetBinding(uint32_t binding) return binding == ~0U ? INVALID_BIND : (uint32_t)binding; } -static bool IsStrippableBuiltin(rdcspv::BuiltIn builtin) +static bool IsStrippableBuiltin(rdcspv::BuiltIn builtin, bool perPrimitive) { + if(perPrimitive && + (builtin == rdcspv::BuiltIn::PrimitiveId || builtin == rdcspv::BuiltIn::Layer || + builtin == rdcspv::BuiltIn::ViewportIndex || builtin == rdcspv::BuiltIn::CullPrimitiveEXT || + builtin == rdcspv::BuiltIn::ShadingRateKHR)) + return true; + return builtin == rdcspv::BuiltIn::PointSize || builtin == rdcspv::BuiltIn::ClipDistance || builtin == rdcspv::BuiltIn::CullDistance; } @@ -871,6 +877,18 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st reflection.dispatchThreadsDimension[2] = e.executionModes.localSize.z; } + { + int idx = e.executionModes.others.indexOf(rdcspv::ExecutionMode::OutputVertices); + if(idx >= 0) + patchData.maxVertices = e.executionModes.others[idx].outputVertices; + } + + { + int idx = e.executionModes.others.indexOf(rdcspv::ExecutionMode::OutputPrimitivesEXT); + if(idx >= 0) + patchData.maxPrimitives = e.executionModes.others[idx].outputPrimitivesEXT; + } + // vulkan spec says "If an object is decorated with the WorkgroupSize decoration, this must take // precedence over any execution mode set for LocalSize." for(auto it : constants) @@ -968,6 +986,10 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st std::set usedIds; std::map> usedStructChildren; + // for arrayed top level builtins like gl_MeshPrimitivesEXT[] there could be an access chain + // first with just the array index, then later the access to the builtin. This map tracks those + // first access chains so the second one can reference the original global + std::map topLevelChildChain; // build the static call tree from the entry point, and build a list of all IDs referenced { @@ -993,10 +1015,37 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st { OpAccessChain access(it); + const DataType &pointeeType = dataTypes[dataTypes[idTypes[access.base]].InnerType()]; + + if(pointeeType.type == DataType::ArrayType) + { + const DataType &innerType = dataTypes[pointeeType.InnerType()]; + + if(innerType.type == DataType::StructType && + (innerType.children[0].decorations.flags & rdcspv::Decorations::HasBuiltIn)) + { + if(access.indexes.size() == 1) + { + topLevelChildChain[access.result] = access.base; + } + else if(access.indexes.size() == 2) + { + usedStructChildren[access.base].insert( + EvaluateConstant(access.indexes[1], specInfo).value.u32v[0]); + } + } + } // save top-level children referenced in structs - if(dataTypes[dataTypes[idTypes[access.base]].InnerType()].type == DataType::StructType) - usedStructChildren[access.base].insert( + else if(pointeeType.type == DataType::StructType) + { + rdcspv::Id globalId = access.base; + + if(topLevelChildChain.find(access.base) != topLevelChildChain.end()) + globalId = topLevelChildChain[access.base]; + + usedStructChildren[globalId].insert( EvaluateConstant(access.indexes[0], specInfo).value.u32v[0]); + } } if(it.opcode() == Op::FunctionCall) @@ -1078,7 +1127,10 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st // // Some compilers generate global variables instead of members of a global struct. If this is // a directly decorated builtin variable which is never used, skip it - if(IsStrippableBuiltin(decorations[global.id].builtIn) && !used) + if(IsStrippableBuiltin( + decorations[global.id].builtIn, + decorations[global.id].others.contains(rdcspv::Decoration::PerPrimitiveEXT)) && + !used) continue; // move to the inner struct if this is an array of structs - e.g. for arrayed shader outputs @@ -1123,7 +1175,9 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st continue; // skip this member if it's unused and of a type that is commonly included 'by accident' - if(IsStrippableBuiltin(structType->children[i].decorations.builtIn) && + if(IsStrippableBuiltin(structType->children[i].decorations.builtIn, + structType->children[i].decorations.others.contains( + rdcspv::Decoration::PerPrimitiveEXT)) && usedchildren.find(i) == usedchildren.end()) continue; @@ -1951,6 +2005,9 @@ void Reflector::AddSignatureParameter(const bool isInput, const ShaderStage stag if(varDecorations.builtIn != BuiltIn::Invalid) sig.systemValue = MakeShaderBuiltin(stage, varDecorations.builtIn); + if(varDecorations.others.contains(rdcspv::Decoration::PerPrimitiveEXT)) + sig.perPrimitiveRate = true; + // fragment shader outputs are implicitly colour outputs. All other builtin outputs do not have a // register index if(stage == ShaderStage::Fragment && !isInput && sig.systemValue == ShaderBuiltin::Undefined) @@ -1981,6 +2038,13 @@ void Reflector::AddSignatureParameter(const bool isInput, const ShaderStage stag if(stage == ShaderStage::Tess_Control) arraySize = 1; + // for mesh shaders too, ignore the root level of array-ness for outputs + if(stage == ShaderStage::Mesh && !isInput) + { + arraySize = 1; + isArray = false; + } + // if this is a root array in the geometry shader, don't reflect it as an array either if(stage == ShaderStage::Geometry && isInput) isArray = false; @@ -2091,6 +2155,21 @@ void Reflector::AddSignatureParameter(const bool isInput, const ShaderStage stag if(isArray) n += StringFormat::Fmt("[%u]", a); + // remove certain common prefixes that generate only useless noise from GLSL. This is mostly + // irrelevant for the majority of cases but is primarily relevant for single-component outputs + // like gl_PointSize or gl_CullPrimitiveEXT + const rdcstr prefixesToRemove[] = { + "gl_PerVertex.", "gl_PerVertex_var.", "gl_MeshVerticesEXT.", + "gl_MeshVerticesEXT_var.", "gl_MeshPrimitivesEXT.", "gl_MeshPrimitivesEXT_var.", + }; + + for(const rdcstr &prefix : prefixesToRemove) + { + int offs = n.find(prefix); + if(offs == 0) + n.erase(0, prefix.length()); + } + sig.varName = n; if(varType->matrix().count <= 1) diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.h b/renderdoc/driver/shaders/spirv/spirv_reflect.h index 3f3929be2..c72b70cba 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.h +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.h @@ -69,6 +69,9 @@ struct SPIRVPatchData // offset of a spec ID. rdcarray specIDs; + // for mesh shaders, the maximum number of vertices/primitives generated by each meshlet + uint32_t maxVertices = 0, maxPrimitives = 0; + // the output topology for tessellation and geometry shaders Topology outTopo = Topology::Unknown; diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index 51ebfc130..9341948fa 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -2670,7 +2670,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, // start with the limits as they are, and subtract off them incrementally. When any limit would // drop below 0, we fail. - uint32_t maxPerStageDescriptorSamplers[6] = { + uint32_t maxPerStageDescriptorSamplers[NumShaderStages] = { + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers, + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers, @@ -2678,7 +2680,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers, }; - uint32_t maxPerStageDescriptorUniformBuffers[6] = { + uint32_t maxPerStageDescriptorUniformBuffers[NumShaderStages] = { + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers, + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers, @@ -2686,7 +2690,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers, }; - uint32_t maxPerStageDescriptorStorageBuffers[6] = { + uint32_t maxPerStageDescriptorStorageBuffers[NumShaderStages] = { + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers, + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers, @@ -2694,7 +2700,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers, }; - uint32_t maxPerStageDescriptorSampledImages[6] = { + uint32_t maxPerStageDescriptorSampledImages[NumShaderStages] = { + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages, + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages, @@ -2702,7 +2710,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages, }; - uint32_t maxPerStageDescriptorStorageImages[6] = { + uint32_t maxPerStageDescriptorStorageImages[NumShaderStages] = { + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages, + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages, @@ -2710,7 +2720,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages, }; - uint32_t maxPerStageDescriptorInputAttachments[6] = { + uint32_t maxPerStageDescriptorInputAttachments[NumShaderStages] = { + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments, + m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments, @@ -2718,7 +2730,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments, m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments, }; - uint32_t maxPerStageResources[6] = { + uint32_t maxPerStageResources[NumShaderStages] = { + m_pDriver->GetDeviceProps().limits.maxPerStageResources, + m_pDriver->GetDeviceProps().limits.maxPerStageResources, m_pDriver->GetDeviceProps().limits.maxPerStageResources, m_pDriver->GetDeviceProps().limits.maxPerStageResources, m_pDriver->GetDeviceProps().limits.maxPerStageResources, @@ -2743,7 +2757,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->GetDeviceProps().limits.maxDescriptorSetInputAttachments; uint32_t maxDescriptorSetInlineUniformBlocks = 0; - uint32_t maxPerStageDescriptorInlineUniformBlocks[6] = {}; + uint32_t maxPerStageDescriptorInlineUniformBlocks[NumShaderStages] = {}; if(m_pDriver->GetExtensions(NULL).ext_EXT_inline_uniform_block) { diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index acc4aa5ba..64fac6ad6 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -352,7 +352,7 @@ struct VulkanCreationInfo rdcarray specialization; }; - Shader shaders[6]; + Shader shaders[NumShaderStages]; // VkPipelineVertexInputStateCreateInfo struct VertBinding diff --git a/renderdoc/driver/vulkan/vk_overlay.cpp b/renderdoc/driver/vulkan/vk_overlay.cpp index bb16fc0f0..6393ae2a7 100644 --- a/renderdoc/driver/vulkan/vk_overlay.cpp +++ b/renderdoc/driver/vulkan/vk_overlay.cpp @@ -2678,6 +2678,8 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D MeshFormat fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::GSOut); if(fmt.vertexResourceId == ResourceId()) fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::VSOut); + if(fmt.vertexResourceId == ResourceId()) + fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::MeshOut); if(fmt.vertexResourceId != ResourceId()) { diff --git a/renderdoc/driver/vulkan/vk_postvs.cpp b/renderdoc/driver/vulkan/vk_postvs.cpp index ac9b8750e..f5010040f 100644 --- a/renderdoc/driver/vulkan/vk_postvs.cpp +++ b/renderdoc/driver/vulkan/vk_postvs.cpp @@ -1515,7 +1515,6 @@ void VulkanReplay::FetchVSOut(uint32_t eventId, VulkanRenderState &state) // set defaults so that we don't try to fetch this output again if something goes wrong and the // same event is selected again { - ret.vsin.topo = state.primitiveTopology; ret.vsout.buf = VK_NULL_HANDLE; ret.vsout.bufmem = VK_NULL_HANDLE; ret.vsout.instStride = 0; @@ -2880,7 +2879,6 @@ void VulkanReplay::FetchVSOut(uint32_t eventId, VulkanRenderState &state) } // fill out m_PostVS.Data - ret.vsin.topo = state.primitiveTopology; ret.vsout.topo = state.primitiveTopology; ret.vsout.buf = meshBuffer; ret.vsout.bufmem = meshMem; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 2a3049a9b..d49176a94 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -265,7 +265,7 @@ rdcarray VulkanReplay::GetPassEvents(uint32_t eventId) // so we don't actually do anything (init postvs/action overlay) // but it's useful to have the first part of the pass as part // of the list - if(start->flags & (ActionFlags::Drawcall | ActionFlags::PassBoundary)) + if(start->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall | ActionFlags::PassBoundary)) passEvents.push_back(start->eventId); start = start->next; @@ -4390,6 +4390,8 @@ void VulkanReplay::BuildTargetShader(ShaderEncoding sourceEncoding, const bytebu case ShaderStage::Geometry: stage = rdcspv::ShaderStage::Geometry; break; case ShaderStage::Pixel: stage = rdcspv::ShaderStage::Fragment; break; case ShaderStage::Compute: stage = rdcspv::ShaderStage::Compute; break; + case ShaderStage::Task: stage = rdcspv::ShaderStage::Task; break; + case ShaderStage::Mesh: stage = rdcspv::ShaderStage::Mesh; break; default: RDCERR("Unexpected type in BuildShader!"); id = ResourceId(); diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index c5af74e05..93019ea5d 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -191,13 +191,14 @@ struct VulkanPostVSData float farPlane; rdcstr status; - } vsin, vsout, gsout; + } vsout, gsout, taskout, meshout; VulkanPostVSData() { - RDCEraseEl(vsin); RDCEraseEl(vsout); RDCEraseEl(gsout); + RDCEraseEl(taskout); + RDCEraseEl(meshout); } const StageData &GetStage(MeshDataStage type) @@ -206,10 +207,14 @@ struct VulkanPostVSData return vsout; else if(type == MeshDataStage::GSOut) return gsout; + else if(type == MeshDataStage::TaskOut) + return taskout; + else if(type == MeshDataStage::MeshOut) + return meshout; else RDCERR("Unexpected mesh data stage!"); - return vsin; + return vsout; } }; @@ -459,6 +464,7 @@ private: void FetchVSOut(uint32_t eventId, VulkanRenderState &state); void FetchTessGSOut(uint32_t eventId, VulkanRenderState &state); + void FetchMeshOut(uint32_t eventId, VulkanRenderState &state); void ClearPostVSCache(); void RefreshDerivedReplacements(); diff --git a/renderdoc/driver/vulkan/vk_shader_cache.cpp b/renderdoc/driver/vulkan/vk_shader_cache.cpp index 473503141..52a700502 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.cpp +++ b/renderdoc/driver/vulkan/vk_shader_cache.cpp @@ -544,8 +544,8 @@ void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &p VulkanResourceManager *rm = m_pDriver->GetResourceManager(); - static VkPipelineShaderStageCreateInfo stages[6]; - static VkSpecializationInfo specInfo[6]; + static VkPipelineShaderStageCreateInfo stages[NumShaderStages]; + static VkSpecializationInfo specInfo[NumShaderStages]; static rdcarray specMapEntries; // the specialization constants can't use more than a uint64_t, so we just over-allocate @@ -553,7 +553,7 @@ void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &p size_t specEntries = 0; - for(uint32_t i = 0; i < 6; i++) + for(uint32_t i = 0; i < NumShaderStages; i++) specEntries += pipeInfo.shaders[i].specialization.size(); specMapEntries.resize(specEntries); @@ -566,7 +566,7 @@ void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &p uint32_t dataOffset = 0; // reserve space for spec constants - for(uint32_t i = 0; i < 6; i++) + for(uint32_t i = 0; i < NumShaderStages; i++) { if(pipeInfo.shaders[i].module != ResourceId()) { diff --git a/renderdoc/driver/vulkan/vk_shader_feedback.cpp b/renderdoc/driver/vulkan/vk_shader_feedback.cpp index ff476ccf8..02a96a438 100644 --- a/renderdoc/driver/vulkan/vk_shader_feedback.cpp +++ b/renderdoc/driver/vulkan/vk_shader_feedback.cpp @@ -1761,14 +1761,14 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) // create vertex shader with modified code VkShaderModuleCreateInfo moduleCreateInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; - VkShaderModule modules[6] = {}; + VkShaderModule modules[NumShaderStages] = {}; - const rdcstr filename[6] = { - "bindless_vertex.spv", "bindless_hull.spv", "bindless_domain.spv", - "bindless_geometry.spv", "bindless_pixel.spv", "bindless_compute.spv", + const rdcstr filename[NumShaderStages] = { + "bindless_vertex.spv", "bindless_hull.spv", "bindless_domain.spv", "bindless_geometry.spv", + "bindless_pixel.spv", "bindless_compute.spv", "bindless_task.spv", "bindless_mesh.spv", }; - std::map printfData[6]; + std::map printfData[NumShaderStages]; if(result.compute) { diff --git a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp index 2190f4871..9adf93147 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp @@ -490,7 +490,7 @@ bool WrappedVulkan::Serialise_vkCreateGraphicsPipelines( GetResourceManager()->AddLiveResource(Pipeline, pipe); VkGraphicsPipelineCreateInfo shadInstantiatedInfo = CreateInfo; - VkPipelineShaderStageCreateInfo shadInstantiations[6]; + VkPipelineShaderStageCreateInfo shadInstantiations[NumShaderStages]; // search for inline shaders, and create shader modules for them so we have objects to pull // out for recreating graphics pipelines (and to replace for shader editing) diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 54d3b48d6..4c1fb4b6d 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -157,6 +157,7 @@ void DoSerialise(SerialiserType &ser, SigParameter &el) SERIALISE_MEMBER(needSemanticIndex); SERIALISE_MEMBER(compCount); SERIALISE_MEMBER(stream); + SERIALISE_MEMBER(perPrimitiveRate); SIZE_CHECK(96); } @@ -290,6 +291,7 @@ void DoSerialise(SerialiserType &ser, ShaderReflection &el) SERIALISE_MEMBER(encoding); SERIALISE_MEMBER(rawBytes); + SERIALISE_MEMBER(outputTopology); SERIALISE_MEMBER(dispatchThreadsDimension); SERIALISE_MEMBER(inputSignature); @@ -306,7 +308,9 @@ void DoSerialise(SerialiserType &ser, ShaderReflection &el) SERIALISE_MEMBER(pointerTypes); - SIZE_CHECK(392); + SERIALISE_MEMBER(taskPayload); + + SIZE_CHECK(456); } template @@ -783,6 +787,25 @@ void DoSerialise(SerialiserType &ser, FrameRecord &el) SIZE_CHECK(528); } +template +void DoSerialise(SerialiserType &ser, TaskGroupSize &el) +{ + SERIALISE_MEMBER(x); + SERIALISE_MEMBER(y); + SERIALISE_MEMBER(z); + + SIZE_CHECK(12); +} + +template +void DoSerialise(SerialiserType &ser, MeshletSize &el) +{ + SERIALISE_MEMBER(numIndices); + SERIALISE_MEMBER(numVertices); + + SIZE_CHECK(8); +} + template void DoSerialise(SerialiserType &ser, MeshFormat &el) { @@ -795,6 +818,11 @@ void DoSerialise(SerialiserType &ser, MeshFormat &el) SERIALISE_MEMBER(vertexByteOffset); SERIALISE_MEMBER(vertexByteStride); SERIALISE_MEMBER(vertexByteSize); + SERIALISE_MEMBER(meshletSizes); + SERIALISE_MEMBER(taskSizes); + SERIALISE_MEMBER(meshletIndexOffset); + SERIALISE_MEMBER(perPrimitiveOffset); + SERIALISE_MEMBER(perPrimitiveStride); SERIALISE_MEMBER(format); SERIALISE_MEMBER(meshColor); SERIALISE_MEMBER(topology); @@ -807,7 +835,7 @@ void DoSerialise(SerialiserType &ser, MeshFormat &el) SERIALISE_MEMBER(showAlpha); SERIALISE_MEMBER(status); - SIZE_CHECK(152); + SIZE_CHECK(224); } template @@ -1029,6 +1057,16 @@ void DoSerialise(SerialiserType &ser, StencilFace &el) SIZE_CHECK(28); } +template +void DoSerialise(SerialiserType &ser, ShaderMeshMessageLocation &el) +{ + SERIALISE_MEMBER(taskGroup); + SERIALISE_MEMBER(meshGroup); + SERIALISE_MEMBER(thread); + + SIZE_CHECK(36); +} + template void DoSerialise(SerialiserType &ser, ShaderComputeMessageLocation &el) { @@ -1071,9 +1109,9 @@ void DoSerialise(SerialiserType &ser, ShaderGeometryMessageLocation &el) template void DoSerialise(SerialiserType &ser, ShaderMessageLocation &el) { - SERIALISE_MEMBER(compute); + SERIALISE_MEMBER(mesh); - SIZE_CHECK(24); + SIZE_CHECK(36); } template @@ -1083,7 +1121,7 @@ void DoSerialise(SerialiserType &ser, ShaderMessage &el) SERIALISE_MEMBER(location); SERIALISE_MEMBER(message); - SIZE_CHECK(56); + SIZE_CHECK(72); } #pragma endregion @@ -1604,6 +1642,8 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::State &el) SERIALISE_MEMBER(geometryShader); SERIALISE_MEMBER(pixelShader); SERIALISE_MEMBER(computeShader); + SERIALISE_MEMBER(ampShader); + SERIALISE_MEMBER(meshShader); SERIALISE_MEMBER(streamOut); @@ -1613,7 +1653,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::State &el) SERIALISE_MEMBER(resourceStates); - SIZE_CHECK(1400); + SIZE_CHECK(1688); } #pragma endregion D3D12 pipeline state @@ -2350,6 +2390,8 @@ void DoSerialise(SerialiserType &ser, VKPipe::State &el) SERIALISE_MEMBER(geometryShader); SERIALISE_MEMBER(fragmentShader); SERIALISE_MEMBER(computeShader); + SERIALISE_MEMBER(taskShader); + SERIALISE_MEMBER(meshShader); SERIALISE_MEMBER(tessellation); @@ -2366,7 +2408,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::State &el) SERIALISE_MEMBER(conditionalRendering); - SIZE_CHECK(2264); + SIZE_CHECK(2712); } #pragma endregion Vulkan pipeline state diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 6c1c1f9fe..0c606bc5c 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -495,7 +495,7 @@ MeshFormat ReplayController::GetPostVSData(uint32_t instID, uint32_t viewID, Mes ActionDescription *action = GetActionByEID(m_EventID); - if(action == NULL || !(action->flags & ActionFlags::Drawcall)) + if(action == NULL || !(action->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall))) return MeshFormat(); instID = RDCMIN(instID, action->numInstances - 1); @@ -1479,6 +1479,8 @@ rdcarray ReplayController::PixelHistory(ResourceId target, ui case ResourceUsage::GS_Constants: case ResourceUsage::PS_Constants: case ResourceUsage::CS_Constants: + case ResourceUsage::TS_Constants: + case ResourceUsage::MS_Constants: case ResourceUsage::All_Constants: case ResourceUsage::VS_Resource: case ResourceUsage::HS_Resource: @@ -1486,6 +1488,8 @@ rdcarray ReplayController::PixelHistory(ResourceId target, ui case ResourceUsage::GS_Resource: case ResourceUsage::PS_Resource: case ResourceUsage::CS_Resource: + case ResourceUsage::TS_Resource: + case ResourceUsage::MS_Resource: case ResourceUsage::All_Resource: case ResourceUsage::InputTarget: case ResourceUsage::CopySrc: @@ -1511,6 +1515,8 @@ rdcarray ReplayController::PixelHistory(ResourceId target, ui case ResourceUsage::GS_RWResource: case ResourceUsage::PS_RWResource: case ResourceUsage::CS_RWResource: + case ResourceUsage::TS_RWResource: + case ResourceUsage::MS_RWResource: case ResourceUsage::All_RWResource: case ResourceUsage::ColorTarget: case ResourceUsage::DepthStencilTarget: @@ -1998,7 +2004,9 @@ rdcpair ReplayController::BuildTargetShader( case ShaderStage::Domain: case ShaderStage::Geometry: case ShaderStage::Pixel: - case ShaderStage::Compute: break; + case ShaderStage::Compute: + case ShaderStage::Task: + case ShaderStage::Mesh: break; default: RDCERR("Unexpected type in BuildShader!"); return rdcpair(); } @@ -2037,7 +2045,9 @@ rdcpair ReplayController::BuildCustomShader( case ShaderStage::Domain: case ShaderStage::Geometry: case ShaderStage::Pixel: - case ShaderStage::Compute: break; + case ShaderStage::Compute: + case ShaderStage::Task: + case ShaderStage::Mesh: break; default: RDCERR("Unexpected type in BuildShader!"); return rdcpair(); } diff --git a/renderdoc/replay/replay_output.cpp b/renderdoc/replay/replay_output.cpp index 6025931df..076bd8061 100644 --- a/renderdoc/replay/replay_output.cpp +++ b/renderdoc/replay/replay_output.cpp @@ -268,7 +268,7 @@ void ReplayOutput::RefreshOverlay() if(m_Type == ReplayOutputType::Mesh) m_OverlayDirty = false; - if(action != NULL && (action->flags & ActionFlags::Drawcall)) + if(action != NULL && (action->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall))) { m_pDevice->InitPostVSBuffers(action->eventId); m_pController->FatalErrorCheck(); @@ -563,9 +563,7 @@ rdcpair ReplayOutput::PickVertex(uint32_t x, uint32_t y) if(!action) return errorReturn; - if(m_RenderData.meshDisplay.type == MeshDataStage::Unknown) - return errorReturn; - if(!(action->flags & ActionFlags::Drawcall)) + if(!(action->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall))) return errorReturn; MeshDisplay cfg = m_RenderData.meshDisplay; @@ -1004,8 +1002,8 @@ void ReplayOutput::DisplayMesh() ActionDescription *action = m_pController->GetActionByEID(m_EventID); if(action == NULL || m_MainOutput.outputID == 0 || m_Width <= 0 || m_Height <= 0 || - (m_RenderData.meshDisplay.type == MeshDataStage::Unknown) || - !(action->flags & ActionFlags::Drawcall)) + (m_RenderData.meshDisplay.position.vertexResourceId == ResourceId()) || + !(action->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall))) { FloatVector color; m_pDevice->BindOutputWindow(m_MainOutput.outputID, false); @@ -1086,6 +1084,12 @@ void ReplayOutput::DisplayMesh() MeshDataStage::VSOut); m_pController->FatalErrorCheck(); } + if(fmt.vertexResourceId == ResourceId()) + { + fmt = m_pDevice->GetPostVSBuffers(passEvents[i], inst, m_RenderData.meshDisplay.curView, + MeshDataStage::MeshOut); + m_pController->FatalErrorCheck(); + } fmt.meshColor = passDraws;