From 1df7c1ae81fd2fd29ae44e20575ec8748d1d9c97 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 26 Oct 2023 12:50:20 +0100 Subject: [PATCH] Implement mesh viewer support for task/mesh shaders --- qrenderdoc/Code/Interface/Extensions.h | 10 + qrenderdoc/Windows/BufferViewer.cpp | 1544 +++++++++++++------ qrenderdoc/Windows/BufferViewer.h | 38 +- qrenderdoc/Windows/BufferViewer.ui | 12 +- renderdoc/data/glsl/glsl_globals.h | 8 +- renderdoc/data/glsl/glsl_ubos.h | 13 + renderdoc/data/glsl/mesh.frag | 2 +- renderdoc/data/glsl/mesh.vert | 66 + renderdoc/data/hlsl/hlsl_cbuffers.h | 17 +- renderdoc/data/hlsl/mesh.hlsl | 78 +- renderdoc/driver/d3d11/d3d11_rendermesh.cpp | 6 + renderdoc/driver/d3d12/d3d12_debug.cpp | 71 + renderdoc/driver/d3d12/d3d12_debug.h | 5 + renderdoc/driver/d3d12/d3d12_rendermesh.cpp | 31 +- renderdoc/driver/vulkan/vk_debug.cpp | 21 +- renderdoc/driver/vulkan/vk_rendermesh.cpp | 95 +- renderdoc/driver/vulkan/vk_replay.h | 1 + renderdoc/driver/vulkan/vk_shader_cache.cpp | 2 +- renderdoc/replay/replay_driver.cpp | 11 + renderdoc/replay/replay_driver.h | 1 + 20 files changed, 1481 insertions(+), 551 deletions(-) diff --git a/qrenderdoc/Code/Interface/Extensions.h b/qrenderdoc/Code/Interface/Extensions.h index efc035e71..bc6f69f49 100644 --- a/qrenderdoc/Code/Interface/Extensions.h +++ b/qrenderdoc/Code/Interface/Extensions.h @@ -133,6 +133,14 @@ DOCUMENT(R"(Specifies the panel to add a menu item into. Adds the item to the context menu for GS/Tess output in the mesh previewing :class:`BufferViewer`. +.. data:: MeshPreview_TaskOutVertex + + Adds the item to the context menu for task shader output in the mesh previewing :class:`BufferViewer`. + +.. data:: MeshPreview_MeshOutVertex + + Adds the item to the context menu for mesh shader output in the mesh previewing :class:`BufferViewer`. + .. data:: TextureViewer_Thumbnail Adds the item to the context menu for all thumbnails in the :class:`TextureViewer`. @@ -153,6 +161,8 @@ enum class ContextMenu MeshPreview_VSInVertex, MeshPreview_VSOutVertex, MeshPreview_GSOutVertex, + MeshPreview_TaskOutVertex, + MeshPreview_MeshOutVertex, TextureViewer_Thumbnail, TextureViewer_InputThumbnail, TextureViewer_OutputThumbnail, diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 50c89e402..31acffb2e 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -483,6 +483,7 @@ struct BufferElementProperties int buffer = 0; ShaderBuiltin systemValue = ShaderBuiltin::Undefined; bool perinstance = false; + bool perprimitive = false; bool floatCastWrong = false; int instancerate = 1; }; @@ -512,6 +513,12 @@ struct BufferConfiguration BufferData *indices = NULL; int32_t baseVertex = 0; + rdcarray taskSizes; + rdcarray meshletVertexPrefixCounts; + uint64_t perPrimitiveOffset = 0; + uint32_t perPrimitiveStride = 0; + Topology topology = Topology::TriangleList; + rdcarray columns; rdcarray props; @@ -553,6 +560,11 @@ struct BufferConfiguration indices->ref(); baseVertex = o.baseVertex; + meshletVertexPrefixCounts = o.meshletVertexPrefixCounts; + taskSizes = o.taskSizes; + perPrimitiveOffset = o.perPrimitiveOffset; + perPrimitiveStride = o.perPrimitiveStride; + topology = o.topology; columns = o.columns; props = o.props; @@ -580,6 +592,9 @@ struct BufferConfiguration for(BufferData *b : buffers) b->deref(); + meshletVertexPrefixCounts.clear(); + taskSizes.clear(); + buffers.clear(); columns.clear(); props.clear(); @@ -1094,6 +1109,20 @@ public: } } + if(role == Qt::BackgroundRole && meshView && !config.meshletVertexPrefixCounts.empty()) + { + auto it = std::upper_bound(config.meshletVertexPrefixCounts.begin(), + config.meshletVertexPrefixCounts.end(), row); + + if(it != config.meshletVertexPrefixCounts.begin()) + it--; + + size_t meshletIdx = it - config.meshletVertexPrefixCounts.begin(); + + return meshletIdx % 2 ? view->palette().color(QPalette::AlternateBase) + : view->palette().color(QPalette::Base); + } + if(role == Qt::DisplayRole) { if(config.numRows == 0 && @@ -1145,7 +1174,24 @@ public: if(col >= 0 && col < totalColumnCount && row < config.numRows) { if(col == 0) - return row + config.pagingOffset; + { + if(meshView && !config.meshletVertexPrefixCounts.empty()) + { + auto it = std::upper_bound(config.meshletVertexPrefixCounts.begin(), + config.meshletVertexPrefixCounts.end(), row); + + if(it != config.meshletVertexPrefixCounts.begin()) + it--; + + size_t meshletIdx = it - config.meshletVertexPrefixCounts.begin(); + + return QFormatStr("%1[%2]").arg(meshletIdx).arg(row + config.pagingOffset - *it); + } + else + { + return row + config.pagingOffset; + } + } uint32_t idx = row; @@ -1188,10 +1234,20 @@ public: const byte *data = config.buffers[prop.buffer]->data(); const byte *end = config.buffers[prop.buffer]->end(); - if(!prop.perinstance) + if(prop.perprimitive) + { + uint32_t prim = row / RENDERDOC_NumVerticesPerPrimitive(config.topology); + data += config.perPrimitiveOffset; + data += config.perPrimitiveStride * prim; + } + else if(!prop.perinstance) + { data += config.buffers[prop.buffer]->stride * idx; + } else + { data += config.buffers[prop.buffer]->stride * instIdx; + } data += el.byteOffset; @@ -1463,26 +1519,23 @@ struct PopulateBufferData { int sequence; - int vsinHoriz; - int vsoutHoriz; - int gsoutHoriz; + int inHoriz; + int out1Horiz; + int out2Horiz; - int vsinVert; - int vsoutVert; - int gsoutVert; + int inVert; + int out1Vert; + int out2Vert; CBufferData cb; - // {VSIn, VSOut, GSOut} x {primary, secondary} + // {In, Out1, Out2} x {primary, secondary} QString highlightNames[6]; bool meshDispatch = false; - BufferConfiguration vsinConfig, vsoutConfig, gsoutConfig; - MeshFormat postVS, postGS; - - BufferConfiguration msoutConfig; - MeshFormat postMS; + BufferConfiguration inConfig, out1Config, out2Config; + MeshFormat postOut1, postOut2; }; struct CalcBoundingBoxData @@ -1530,6 +1583,9 @@ void CacheDataForIteration(QVector &cache, const rdcarray 4 ? 8U : 4U; - if(ctx.CurPipelineState().HasAlignedPostVSData( - shader->stage == ShaderStage::Vertex ? MeshDataStage::VSOut : MeshDataStage::GSOut)) + MeshDataStage outStage = MeshDataStage::VSOut; + + switch(shader->stage) + { + case ShaderStage::Vertex: outStage = MeshDataStage::VSOut; break; + case ShaderStage::Hull: outStage = MeshDataStage::GSOut; break; + case ShaderStage::Domain: outStage = MeshDataStage::GSOut; break; + case ShaderStage::Geometry: outStage = MeshDataStage::GSOut; break; + case ShaderStage::Task: outStage = MeshDataStage::TaskOut; break; + case ShaderStage::Mesh: outStage = MeshDataStage::MeshOut; break; + default: break; + } + + uint32_t &offset = prop.perprimitive ? perPrimOffset : perVertOffset; + + if(ctx.CurPipelineState().HasAlignedPostVSData(outStage)) { if(numComps == 2) offset = AlignUp(offset, 2U * elemSize); @@ -1636,35 +1707,47 @@ static void ConfigureColumnsForShader(ICaptureContext &ctx, int32_t streamSelect static void ConfigureColumnsForMeshPipe(ICaptureContext &ctx, PopulateBufferData *bufdata) { - bufdata->vsinConfig.numRows = 0; - bufdata->vsinConfig.unclampedNumRows = 0; + bufdata->inConfig.statusString = lit("No input visualisation supported for mesh shaders"); - bufdata->vsinConfig.noVertices = true; - bufdata->vsinConfig.noInstances = true; + ConfigureStatusColumn(bufdata->inConfig.columns, bufdata->inConfig.props); + + const ShaderReflection *ts = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Task); + + if(ts && bufdata->out1Config.statusString.isEmpty()) + { + bufdata->out1Config.columns = ts->taskPayload.variables; + bufdata->out1Config.props.resize(bufdata->out1Config.columns.size()); + } + else + { + if(bufdata->out1Config.statusString.isEmpty()) + bufdata->out1Config.statusString = lit("No output visualisation supported for task shaders"); + ConfigureStatusColumn(bufdata->out1Config.columns, bufdata->out1Config.props); + } const ShaderReflection *ms = ctx.CurPipelineState().GetShaderReflection(ShaderStage::Mesh); - ConfigureColumnsForShader(ctx, 0, ms, bufdata->vsoutConfig.columns, bufdata->vsoutConfig.props); + ConfigureColumnsForShader(ctx, 0, ms, bufdata->out2Config.columns, bufdata->out2Config.props); } static void ConfigureColumnsForVertexPipe(ICaptureContext &ctx, PopulateBufferData *bufdata) { const ActionDescription *action = ctx.CurAction(); - bufdata->vsinConfig.numRows = 0; - bufdata->vsinConfig.unclampedNumRows = 0; + bufdata->inConfig.numRows = 0; + bufdata->inConfig.unclampedNumRows = 0; - bufdata->vsinConfig.noVertices = false; - bufdata->vsinConfig.noInstances = false; + bufdata->inConfig.noVertices = false; + bufdata->inConfig.noInstances = false; rdcarray vinputs = ctx.CurPipelineState().GetVertexInputs(); - bufdata->vsinConfig.columns.reserve(vinputs.count()); - bufdata->vsinConfig.columns.clear(); - bufdata->vsinConfig.props.reserve(vinputs.count()); - bufdata->vsinConfig.props.clear(); - bufdata->vsinConfig.genericsEnabled.resize(vinputs.count()); - bufdata->vsinConfig.generics.resize(vinputs.count()); + bufdata->inConfig.columns.reserve(vinputs.count()); + bufdata->inConfig.columns.clear(); + bufdata->inConfig.props.reserve(vinputs.count()); + bufdata->inConfig.props.clear(); + bufdata->inConfig.genericsEnabled.resize(vinputs.count()); + bufdata->inConfig.generics.resize(vinputs.count()); for(const VertexInputAttribute &a : vinputs) { @@ -1685,20 +1768,20 @@ static void ConfigureColumnsForVertexPipe(ICaptureContext &ctx, PopulateBufferDa p.floatCastWrong = a.floatCastWrong; p.format = a.format; - bufdata->vsinConfig.genericsEnabled[bufdata->vsinConfig.columns.count()] = false; + bufdata->inConfig.genericsEnabled[bufdata->inConfig.columns.count()] = false; if(a.genericEnabled) { - bufdata->vsinConfig.genericsEnabled[bufdata->vsinConfig.columns.count()] = true; - bufdata->vsinConfig.generics[bufdata->vsinConfig.columns.count()] = a.genericValue; + bufdata->inConfig.genericsEnabled[bufdata->inConfig.columns.count()] = true; + bufdata->inConfig.generics[bufdata->inConfig.columns.count()] = a.genericValue; } - bufdata->vsinConfig.columns.push_back(f); - bufdata->vsinConfig.props.push_back(p); + bufdata->inConfig.columns.push_back(f); + bufdata->inConfig.props.push_back(p); } - bufdata->vsinConfig.numRows = action->numIndices; - bufdata->vsinConfig.unclampedNumRows = 0; + bufdata->inConfig.numRows = action->numIndices; + bufdata->inConfig.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. @@ -1770,34 +1853,34 @@ static void ConfigureColumnsForVertexPipe(ICaptureContext &ctx, PopulateBufferDa } // if we have significantly clamped, then set the unclamped number of rows and clamp. - if(numRowsUpperBound != ~0U && numRowsUpperBound + 100 < bufdata->vsinConfig.numRows) + if(numRowsUpperBound != ~0U && numRowsUpperBound + 100 < bufdata->inConfig.numRows) { - bufdata->vsinConfig.unclampedNumRows = bufdata->vsinConfig.numRows; - bufdata->vsinConfig.numRows = numRowsUpperBound + 100; + bufdata->inConfig.unclampedNumRows = bufdata->inConfig.numRows; + bufdata->inConfig.numRows = numRowsUpperBound + 100; } if((action->flags & ActionFlags::Drawcall) && action->numIndices == 0) - bufdata->vsinConfig.noVertices = true; + bufdata->inConfig.noVertices = true; if((action->flags & ActionFlags::Instanced) && action->numInstances == 0) { - bufdata->vsinConfig.noInstances = true; - bufdata->vsinConfig.numRows = bufdata->vsinConfig.unclampedNumRows = 0; + bufdata->inConfig.noInstances = true; + bufdata->inConfig.numRows = bufdata->inConfig.unclampedNumRows = 0; } - bufdata->vsoutConfig.columns.clear(); - bufdata->vsoutConfig.props.clear(); - bufdata->gsoutConfig.columns.clear(); - bufdata->gsoutConfig.props.clear(); + bufdata->out1Config.columns.clear(); + bufdata->out1Config.props.clear(); + bufdata->out2Config.columns.clear(); + bufdata->out2Config.props.clear(); 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, 0, vs, bufdata->out1Config.columns, bufdata->out1Config.props); ConfigureColumnsForShader(ctx, ctx.CurPipelineState().GetRasterizedStream(), last, - bufdata->gsoutConfig.columns, bufdata->gsoutConfig.props); + bufdata->out2Config.columns, bufdata->out2Config.props); } static void ConfigureColumns(ICaptureContext &ctx, PopulateBufferData *bufdata) @@ -1816,24 +1899,106 @@ static void ConfigureColumns(ICaptureContext &ctx, PopulateBufferData *bufdata) { IEventBrowser *eb = ctx.GetEventBrowser(); - bufdata->vsinConfig.statusString = bufdata->vsoutConfig.statusString = - bufdata->gsoutConfig.statusString = + bufdata->inConfig.statusString = bufdata->out1Config.statusString = + bufdata->out2Config.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); + ConfigureStatusColumn(bufdata->inConfig.columns, bufdata->inConfig.props); + ConfigureStatusColumn(bufdata->out1Config.columns, bufdata->out1Config.props); + ConfigureStatusColumn(bufdata->out2Config.columns, bufdata->out2Config.props); - bufdata->vsinConfig.genericsEnabled.push_back(false); - bufdata->vsinConfig.generics.push_back(PixelValue()); + bufdata->inConfig.genericsEnabled.push_back(false); + bufdata->inConfig.generics.push_back(PixelValue()); } } -static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, PopulateBufferData *data) +static void RT_FetchMeshPipeData(IReplayController *r, ICaptureContext &ctx, PopulateBufferData *data) +{ + uint32_t numIndices = data->postOut2.numIndices; + + if(data->inConfig.indices) + data->inConfig.indices->deref(); + + data->inConfig.indices = NULL; + + data->out1Config.numRows = data->postOut1.numIndices; + data->out1Config.unclampedNumRows = 0; + + if(data->out1Config.indices) + data->out1Config.indices->deref(); + if(data->out1Config.displayIndices) + data->out1Config.displayIndices->deref(); + data->out1Config.displayIndices = NULL; + + data->out1Config.taskSizes = data->postOut1.taskSizes; + + if(data->postOut1.vertexResourceId != ResourceId()) + { + BufferData *postts = new BufferData; + postts->storage = + r->GetBufferData(data->postOut1.vertexResourceId, data->postOut1.vertexByteOffset, 0); + + postts->stride = data->postOut1.vertexByteStride; + + // ref passes to model + data->out1Config.buffers.push_back(postts); + } + + data->out1Config.statusString = data->postOut1.status; + + data->out2Config.numRows = data->postOut2.numIndices; + data->out2Config.unclampedNumRows = 0; + + if(data->out2Config.indices) + data->out2Config.indices->deref(); + if(data->out2Config.displayIndices) + data->out2Config.displayIndices->deref(); + data->out2Config.displayIndices = NULL; + + uint32_t count = 0; + for(const MeshletSize &meshletSize : data->postOut2.meshletSizes) + { + data->out2Config.meshletVertexPrefixCounts.push_back(count); + count += meshletSize.numIndices; + } + + data->out2Config.topology = data->postOut2.topology; + data->out2Config.perPrimitiveOffset = data->postOut2.perPrimitiveOffset; + data->out2Config.perPrimitiveStride = data->postOut2.perPrimitiveStride; + + bytebuf idata = r->GetBufferData(data->postOut2.indexResourceId, data->postOut2.indexByteOffset, + numIndices * data->postOut2.indexByteStride); + + data->out2Config.indices = new BufferData(); + data->out2Config.indices->storage.resize(sizeof(uint32_t) * numIndices); + uint32_t *indices = (uint32_t *)data->out2Config.indices->data(); + + memcpy(indices, idata.data(), qMin(idata.size(), numIndices * sizeof(uint32_t))); + + if(data->postOut2.vertexResourceId != ResourceId()) + { + BufferData *postms = new BufferData; + postms->storage = + r->GetBufferData(data->postOut2.vertexResourceId, data->postOut2.vertexByteOffset, 0); + + postms->stride = data->postOut2.vertexByteStride; + + // ref passes to model + data->out2Config.buffers.push_back(postms); + } + + data->out2Config.perPrimitiveOffset = data->postOut2.perPrimitiveOffset; + data->out2Config.perPrimitiveStride = data->postOut2.perPrimitiveStride; + + data->out2Config.statusString = data->postOut2.status; +} + +static void RT_FetchVertexPipeData(IReplayController *r, ICaptureContext &ctx, + PopulateBufferData *data) { const ActionDescription *action = ctx.CurAction(); @@ -1858,19 +2023,19 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat idata = r->GetBufferData(ib.resourceId, ib.byteOffset + offset, readBytes); } - if(data->vsinConfig.indices) - data->vsinConfig.indices->deref(); + if(data->inConfig.indices) + data->inConfig.indices->deref(); - data->vsinConfig.indices = new BufferData(); + data->inConfig.indices = new BufferData(); if(action && ib.byteStride != 0 && !idata.isEmpty()) - data->vsinConfig.indices->storage.resize( + data->inConfig.indices->storage.resize( sizeof(uint32_t) * qMin(numIndices, (((uint32_t)idata.size() + ib.byteStride - 1) / ib.byteStride))); else if(action && (action->flags & ActionFlags::Indexed)) - data->vsinConfig.indices->storage.resize(sizeof(uint32_t)); + data->inConfig.indices->storage.resize(sizeof(uint32_t)); - uint32_t *indices = (uint32_t *)data->vsinConfig.indices->data(); + uint32_t *indices = (uint32_t *)data->inConfig.indices->data(); uint32_t maxIndex = 0; if(action) @@ -1881,7 +2046,7 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat maxIndex = 0; if(ib.byteStride == 1) { - uint8_t primRestart = data->vsinConfig.primRestart & 0xff; + uint8_t primRestart = data->inConfig.primRestart & 0xff; for(size_t i = 0; i < idata.size() && (uint32_t)i < numIndices; i++) { @@ -1894,7 +2059,7 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat } else if(ib.byteStride == 2) { - uint16_t primRestart = data->vsinConfig.primRestart & 0xffff; + uint16_t primRestart = data->inConfig.primRestart & 0xffff; uint16_t *src = (uint16_t *)idata.data(); for(size_t i = 0; i < idata.size() / sizeof(uint16_t) && (uint32_t)i < numIndices; i++) @@ -1908,7 +2073,7 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat } else if(ib.byteStride == 4) { - uint32_t primRestart = data->vsinConfig.primRestart; + uint32_t primRestart = data->inConfig.primRestart; memcpy(indices, idata.data(), qMin(idata.size(), numIndices * sizeof(uint32_t))); @@ -1931,10 +2096,10 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat uint32_t maxAttrOffset = 0; - for(int c = 0; c < data->vsinConfig.columns.count(); c++) + for(int c = 0; c < data->inConfig.columns.count(); c++) { - const ShaderConstant &col = data->vsinConfig.columns[c]; - const BufferElementProperties &prop = data->vsinConfig.props[c]; + const ShaderConstant &col = data->inConfig.columns[c]; + const BufferElementProperties &prop = data->inConfig.props[c]; if(prop.buffer == vbIdx) { @@ -1997,47 +2162,48 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat buf->stride = vb.byteStride; } // ref passes to model - data->vsinConfig.buffers.push_back(buf); + data->inConfig.buffers.push_back(buf); } - if(data->postVS.numIndices <= data->vsinConfig.numRows) + if(data->postOut1.numIndices <= data->inConfig.numRows) { - data->vsoutConfig.numRows = data->postVS.numIndices; - data->vsoutConfig.unclampedNumRows = 0; + data->out1Config.numRows = data->postOut1.numIndices; + data->out1Config.unclampedNumRows = 0; } else { // the vertex shader can't run any expansion, so apply the same clamping to it as we applied to // the inputs. This protects against draws with an invalid number of vertices. - data->vsoutConfig.numRows = data->vsinConfig.numRows; - data->vsoutConfig.unclampedNumRows = data->vsinConfig.unclampedNumRows; + data->out1Config.numRows = data->inConfig.numRows; + data->out1Config.unclampedNumRows = data->inConfig.unclampedNumRows; } - data->vsoutConfig.statusString = data->postVS.status; + data->out1Config.statusString = data->postOut1.status; - data->vsoutConfig.baseVertex = data->postVS.baseVertex; - data->vsoutConfig.displayBaseVertex = data->vsinConfig.baseVertex; + data->out1Config.baseVertex = data->postOut1.baseVertex; + data->out1Config.displayBaseVertex = data->inConfig.baseVertex; - if(action && data->postVS.indexResourceId != ResourceId() && (action->flags & ActionFlags::Indexed)) - idata = r->GetBufferData(data->postVS.indexResourceId, data->postVS.indexByteOffset, - numIndices * data->postVS.indexByteStride); + if(action && data->postOut1.indexResourceId != ResourceId() && + (action->flags & ActionFlags::Indexed)) + idata = r->GetBufferData(data->postOut1.indexResourceId, data->postOut1.indexByteOffset, + numIndices * data->postOut1.indexByteStride); indices = NULL; - if(data->vsoutConfig.indices) - data->vsoutConfig.indices->deref(); - if(data->vsoutConfig.displayIndices) - data->vsoutConfig.displayIndices->deref(); + if(data->out1Config.indices) + data->out1Config.indices->deref(); + if(data->out1Config.displayIndices) + data->out1Config.displayIndices->deref(); { // display the same index values - data->vsoutConfig.displayIndices = data->vsinConfig.indices; - data->vsoutConfig.displayIndices->ref(); + data->out1Config.displayIndices = data->inConfig.indices; + data->out1Config.displayIndices->ref(); - data->vsoutConfig.indices = new BufferData(); + data->out1Config.indices = new BufferData(); if(action && ib.byteStride != 0 && !idata.isEmpty()) { - data->vsoutConfig.indices->storage.resize(sizeof(uint32_t) * numIndices); - indices = (uint32_t *)data->vsoutConfig.indices->data(); + data->out1Config.indices->storage.resize(sizeof(uint32_t) * numIndices); + indices = (uint32_t *)data->out1Config.indices->data(); if(ib.byteStride == 1) { @@ -2057,38 +2223,38 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat } } - if(data->postVS.vertexResourceId != ResourceId()) + if(data->postOut1.vertexResourceId != ResourceId()) { BufferData *postvs = new BufferData; postvs->storage = - r->GetBufferData(data->postVS.vertexResourceId, data->postVS.vertexByteOffset, 0); + r->GetBufferData(data->postOut1.vertexResourceId, data->postOut1.vertexByteOffset, 0); - postvs->stride = data->postVS.vertexByteStride; + postvs->stride = data->postOut1.vertexByteStride; // ref passes to model - data->vsoutConfig.buffers.push_back(postvs); + data->out1Config.buffers.push_back(postvs); } - data->gsoutConfig.statusString = data->postGS.status; + data->out2Config.statusString = data->postOut2.status; - data->gsoutConfig.numRows = data->postGS.numIndices; - data->gsoutConfig.unclampedNumRows = 0; - data->gsoutConfig.baseVertex = data->postGS.baseVertex; - data->gsoutConfig.displayBaseVertex = data->vsinConfig.baseVertex; + data->out2Config.numRows = data->postOut2.numIndices; + data->out2Config.unclampedNumRows = 0; + data->out2Config.baseVertex = data->postOut2.baseVertex; + data->out2Config.displayBaseVertex = data->inConfig.baseVertex; indices = NULL; - data->gsoutConfig.indices = NULL; + data->out2Config.indices = NULL; - if(data->postGS.vertexResourceId != ResourceId()) + if(data->postOut2.vertexResourceId != ResourceId()) { BufferData *postgs = new BufferData; postgs->storage = - r->GetBufferData(data->postGS.vertexResourceId, data->postGS.vertexByteOffset, 0); + r->GetBufferData(data->postOut2.vertexResourceId, data->postOut2.vertexByteOffset, 0); - postgs->stride = data->postGS.vertexByteStride; + postgs->stride = data->postOut2.vertexByteStride; // ref passes to model - data->gsoutConfig.buffers.push_back(postgs); + data->out2Config.buffers.push_back(postgs); } } @@ -2183,9 +2349,37 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) byteRangeStart->setMinimum(0ULL); byteRangeLength->setMinimum(0ULL); - m_ModelVSIn = new BufferItemModel(ui->vsinData, true, meshview, this); - m_ModelVSOut = new BufferItemModel(ui->vsoutData, false, meshview, this); - m_ModelGSOut = new BufferItemModel(ui->gsoutData, false, meshview, this); + m_ModelIn = new BufferItemModel(ui->inTable, true, meshview, this); + m_ModelOut1 = new BufferItemModel(ui->out1Table, false, meshview, this); + m_ModelOut2 = new BufferItemModel(ui->out2Table, false, meshview, this); + + // we keep the old UI names for serialised layouts compatibility + QString containerNames[] = { + lit("vsinData"), + lit("vsoutData"), + lit("gsoutData"), + }; + + for(size_t i = 0; i < 3; i++) + { + m_Containers[i] = new QWidget(this); + // for layout compatibility + m_Containers[i]->setObjectName(containerNames[i]); + + QVBoxLayout *layout = new QVBoxLayout(m_Containers[i]); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + } + + if(meshview) + { + m_Containers[0]->layout()->addWidget(ui->inTable); + m_Containers[0]->layout()->addWidget(ui->fixedVars); + m_Containers[1]->layout()->addWidget(ui->out1Table); + m_Containers[2]->layout()->addWidget(ui->out2Table); + + ui->fixedVars->setVisible(false); + } m_MeshView = meshview; @@ -2204,9 +2398,9 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) ui->outputTabs->setCurrentIndex(0); m_CurStage = MeshDataStage::VSIn; - ui->vsinData->setFont(Formatter::FixedFont()); - ui->vsoutData->setFont(Formatter::FixedFont()); - ui->gsoutData->setFont(Formatter::FixedFont()); + ui->inTable->setFont(Formatter::FixedFont()); + ui->out1Table->setFont(Formatter::FixedFont()); + ui->out2Table->setFont(Formatter::FixedFont()); ui->minBoundsLabel->setFont(Formatter::FixedFont()); ui->maxBoundsLabel->setFont(Formatter::FixedFont()); @@ -2250,38 +2444,40 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) QObject::connect(ui->exportDrop, &QToolButton::clicked, [this] { exportData(BufferExport(BufferExport::CSV)); }); - ui->vsinData->setContextMenuPolicy(Qt::CustomContextMenu); - ui->vsoutData->setContextMenuPolicy(Qt::CustomContextMenu); - ui->gsoutData->setContextMenuPolicy(Qt::CustomContextMenu); + ui->inTable->setContextMenuPolicy(Qt::CustomContextMenu); + ui->out1Table->setContextMenuPolicy(Qt::CustomContextMenu); + ui->out2Table->setContextMenuPolicy(Qt::CustomContextMenu); ui->fixedVars->setContextMenuPolicy(Qt::CustomContextMenu); + ui->fixedVars->setFrameShape(QFrame::NoFrame); + QMenu *menu = new QMenu(this); - ui->vsinData->setCustomHeaderSizing(true); - ui->vsoutData->setCustomHeaderSizing(true); - ui->gsoutData->setCustomHeaderSizing(true); + ui->inTable->setCustomHeaderSizing(true); + ui->out1Table->setCustomHeaderSizing(true); + ui->out2Table->setCustomHeaderSizing(true); - ui->vsinData->setAllowKeyboardSearches(false); - ui->vsoutData->setAllowKeyboardSearches(false); - ui->gsoutData->setAllowKeyboardSearches(false); + ui->inTable->setAllowKeyboardSearches(false); + ui->out1Table->setAllowKeyboardSearches(false); + ui->out2Table->setAllowKeyboardSearches(false); QObject::connect(ui->fixedVars, &RDTreeWidget::customContextMenuRequested, this, &BufferViewer::fixedVars_contextMenu); - QObject::connect(ui->vsinData, &RDTableView::customContextMenuRequested, + QObject::connect(ui->inTable, &RDTableView::customContextMenuRequested, [this, menu](const QPoint &pos) { stageRowMenu(MeshDataStage::VSIn, menu, pos); }); menu = new QMenu(this); QObject::connect( - ui->vsoutData, &RDTableView::customContextMenuRequested, + ui->out1Table, &RDTableView::customContextMenuRequested, [this, menu](const QPoint &pos) { stageRowMenu(MeshDataStage::VSOut, menu, pos); }); menu = new QMenu(this); QObject::connect( - ui->gsoutData, &RDTableView::customContextMenuRequested, + ui->out2Table, &RDTableView::customContextMenuRequested, [this, menu](const QPoint &pos) { stageRowMenu(MeshDataStage::GSOut, menu, pos); }); ui->dockarea->setAllowFloatingWindow(false); @@ -2291,6 +2487,7 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) configureDrawRange(); + ui->solidShading->clear(); ui->solidShading->addItems({tr("None"), tr("Solid Colour"), tr("Flat Shaded"), tr("Secondary")}); ui->solidShading->adjustSize(); ui->solidShading->setCurrentIndex(0); @@ -2310,33 +2507,33 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) on_controlType_currentIndexChanged(0); - QObject::connect(ui->vsinData->selectionModel(), &QItemSelectionModel::selectionChanged, this, + QObject::connect(ui->inTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BufferViewer::data_selected); - QObject::connect(ui->vsoutData->selectionModel(), &QItemSelectionModel::selectionChanged, this, + QObject::connect(ui->out1Table->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BufferViewer::data_selected); - QObject::connect(ui->gsoutData->selectionModel(), &QItemSelectionModel::selectionChanged, this, + QObject::connect(ui->out2Table->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BufferViewer::data_selected); - m_CurView = ui->vsinData; + m_CurView = ui->inTable; m_CurFixed = false; - QObject::connect(ui->vsinData, &RDTableView::clicked, [this]() { - m_CurView = ui->vsinData; + QObject::connect(ui->inTable, &RDTableView::clicked, [this]() { + m_CurView = ui->inTable; m_CurFixed = false; }); - QObject::connect(ui->vsoutData, &RDTableView::clicked, [this]() { m_CurView = ui->vsoutData; }); - QObject::connect(ui->gsoutData, &RDTableView::clicked, [this]() { m_CurView = ui->gsoutData; }); + QObject::connect(ui->out1Table, &RDTableView::clicked, [this]() { m_CurView = ui->out1Table; }); + QObject::connect(ui->out2Table, &RDTableView::clicked, [this]() { m_CurView = ui->out2Table; }); QObject::connect(ui->fixedVars, &RDTreeWidget::clicked, [this]() { m_CurView = NULL; m_CurFixed = true; }); - QObject::connect(ui->vsinData->verticalScrollBar(), &QScrollBar::valueChanged, this, + QObject::connect(ui->inTable->verticalScrollBar(), &QScrollBar::valueChanged, this, &BufferViewer::data_scrolled); - QObject::connect(ui->vsoutData->verticalScrollBar(), &QScrollBar::valueChanged, this, + QObject::connect(ui->out1Table->verticalScrollBar(), &QScrollBar::valueChanged, this, &BufferViewer::data_scrolled); - QObject::connect(ui->gsoutData->verticalScrollBar(), &QScrollBar::valueChanged, this, + QObject::connect(ui->out2Table->verticalScrollBar(), &QScrollBar::valueChanged, this, &BufferViewer::data_scrolled); QObject::connect(ui->fovGuess, OverloadedSlot::of(&QDoubleSpinBox::valueChanged), this, @@ -2384,8 +2581,8 @@ void BufferViewer::SetupRawView() { ui->formatSpecifier->setVisible(true); ui->outputTabs->setVisible(false); - ui->vsoutData->setVisible(false); - ui->gsoutData->setVisible(false); + ui->out1Table->setVisible(false); + ui->out2Table->setVisible(false); // hide buttons we don't want in the toolbar ui->syncViews->setVisible(false); @@ -2395,18 +2592,17 @@ void BufferViewer::SetupRawView() ui->viewIndex->setVisible(false); ui->dockarea->setVisible(false); - ui->vsinData->setFrameShape(QFrame::NoFrame); - ui->fixedVars->setFrameShape(QFrame::NoFrame); + ui->inTable->setFrameShape(QFrame::NoFrame); - ui->vsinData->setPinnedColumns(1); - ui->vsinData->setColumnGroupRole(columnGroupRole); + ui->inTable->setPinnedColumns(1); + ui->inTable->setColumnGroupRole(columnGroupRole); - m_delegate = new RichTextViewDelegate(ui->vsinData); - ui->vsinData->setItemDelegate(m_delegate); + m_delegate = new RichTextViewDelegate(ui->inTable); + ui->inTable->setItemDelegate(m_delegate); - ui->vsinData->viewport()->installEventFilter(this); + ui->inTable->viewport()->installEventFilter(this); - ui->vsinData->setMouseTracking(true); + ui->inTable->setMouseTracking(true); ui->formatSpecifier->setWindowTitle(tr("Buffer Format")); @@ -2482,15 +2678,13 @@ void BufferViewer::SetupRawView() m_VLayout->addWidget(ui->meshToolbar); // 0 will be variable, but set it to something here so QSplitter doesn't barf - m_OuterSplitter->insertWidget(0, ui->vsinData); + m_OuterSplitter->insertWidget(0, ui->inTable); m_OuterSplitter->insertWidget(1, ui->formatSpecifier); m_VLayout->addWidget(m_OuterSplitter); } void BufferViewer::SetupMeshView() { - setWindowTitle(tr("Mesh Viewer")); - // hide buttons we don't want in the toolbar ui->byteRangeLine->setVisible(false); ui->byteRangeStartLabel->setVisible(false); @@ -2501,6 +2695,14 @@ void BufferViewer::SetupMeshView() ui->fixedVars->setVisible(false); ui->showPadding->setVisible(false); + ui->fixedVars->setColumns({tr("Name"), tr("Value"), tr("Type")}); + { + ui->fixedVars->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + ui->fixedVars->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + } + + ui->fixedVars->setFont(Formatter::FixedFont()); + ui->resourceDetails->setVisible(false); ui->formatSpecifier->setVisible(false); ui->cameraControlsGroup->setVisible(false); @@ -2512,28 +2714,27 @@ void BufferViewer::SetupMeshView() ui->dockarea->addToolWindow(ui->outputTabs, ToolWindowManager::EmptySpace); ui->dockarea->setToolWindowProperties(ui->outputTabs, ToolWindowManager::HideCloseButton); - ui->vsinData->setWindowTitle(tr("VS Input")); - ui->vsinData->setFrameShape(QFrame::NoFrame); + ui->inTable->setFrameShape(QFrame::NoFrame); ui->dockarea->addToolWindow( - ui->vsinData, ToolWindowManager::AreaReference(ToolWindowManager::TopOf, - ui->dockarea->areaOf(ui->outputTabs), 0.5f)); - ui->dockarea->setToolWindowProperties(ui->vsinData, ToolWindowManager::HideCloseButton); + m_Containers[0], ToolWindowManager::AreaReference( + ToolWindowManager::TopOf, ui->dockarea->areaOf(ui->outputTabs), 0.5f)); + ui->dockarea->setToolWindowProperties(ui->inTable, ToolWindowManager::HideCloseButton); - ui->vsoutData->setWindowTitle(tr("VS Output")); - ui->vsoutData->setFrameShape(QFrame::NoFrame); + ui->out1Table->setFrameShape(QFrame::NoFrame); ui->dockarea->addToolWindow( - ui->vsoutData, ToolWindowManager::AreaReference(ToolWindowManager::RightOf, - ui->dockarea->areaOf(ui->vsinData), 0.5f)); - ui->dockarea->setToolWindowProperties(ui->vsoutData, ToolWindowManager::HideCloseButton); + m_Containers[1], ToolWindowManager::AreaReference( + ToolWindowManager::RightOf, ui->dockarea->areaOf(m_Containers[0]), 0.5f)); + ui->dockarea->setToolWindowProperties(m_Containers[1], ToolWindowManager::HideCloseButton); - ui->gsoutData->setWindowTitle(tr("GS/DS Output")); - ui->gsoutData->setFrameShape(QFrame::NoFrame); + ui->out2Table->setFrameShape(QFrame::NoFrame); ui->dockarea->addToolWindow( - ui->gsoutData, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, - ui->dockarea->areaOf(ui->vsoutData), 0.5f)); - ui->dockarea->setToolWindowProperties(ui->gsoutData, ToolWindowManager::HideCloseButton); + m_Containers[2], ToolWindowManager::AreaReference( + ToolWindowManager::AddTo, ui->dockarea->areaOf(m_Containers[1]), 0.5f)); + ui->dockarea->setToolWindowProperties(ui->out2Table, ToolWindowManager::HideCloseButton); - ToolWindowManager::raiseToolWindow(ui->vsoutData); + ToolWindowManager::raiseToolWindow(m_Containers[1]); + + updateLabelsAndLayout(); m_HeaderMenu = new QMenu(this); @@ -2554,7 +2755,7 @@ void BufferViewer::SetupMeshView() model->setPosColumn(-1); model->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); - UI_CalculateMeshFormats(); + UI_ConfigureFormats(); on_resetCamera_clicked(); UpdateCurrentMeshConfig(); INVOKE_MEMFN(RT_UpdateAndDisplay); @@ -2564,7 +2765,7 @@ void BufferViewer::SetupMeshView() model->setPosColumn(m_ContextColumn); - UI_CalculateMeshFormats(); + UI_ConfigureFormats(); on_resetCamera_clicked(); UpdateCurrentMeshConfig(); INVOKE_MEMFN(RT_UpdateAndDisplay); @@ -2575,7 +2776,7 @@ void BufferViewer::SetupMeshView() model->setSecondaryColumn(m_ContextColumn, m_Config.solidShadeMode == SolidShade::Secondary, false); - UI_CalculateMeshFormats(); + UI_ConfigureFormats(); UpdateCurrentMeshConfig(); INVOKE_MEMFN(RT_UpdateAndDisplay); }); @@ -2584,28 +2785,28 @@ void BufferViewer::SetupMeshView() model->setSecondaryColumn(m_ContextColumn, m_Config.solidShadeMode == SolidShade::Secondary, true); - UI_CalculateMeshFormats(); + UI_ConfigureFormats(); UpdateCurrentMeshConfig(); INVOKE_MEMFN(RT_UpdateAndDisplay); }); - ui->vsinData->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - ui->vsoutData->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - ui->gsoutData->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + ui->inTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + ui->out1Table->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + ui->out2Table->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - ui->vsinData->setPinnedColumns(2); - ui->vsoutData->setPinnedColumns(2); - ui->gsoutData->setPinnedColumns(2); + ui->inTable->setPinnedColumns(2); + ui->out1Table->setPinnedColumns(2); + ui->out2Table->setPinnedColumns(2); - ui->vsinData->setColumnGroupRole(columnGroupRole); - ui->vsoutData->setColumnGroupRole(columnGroupRole); - ui->gsoutData->setColumnGroupRole(columnGroupRole); + ui->inTable->setColumnGroupRole(columnGroupRole); + ui->out1Table->setColumnGroupRole(columnGroupRole); + ui->out2Table->setColumnGroupRole(columnGroupRole); - QObject::connect(ui->vsinData->horizontalHeader(), &QHeaderView::customContextMenuRequested, + QObject::connect(ui->inTable->horizontalHeader(), &QHeaderView::customContextMenuRequested, [this](const QPoint &pos) { meshHeaderMenu(MeshDataStage::VSIn, pos); }); - QObject::connect(ui->vsoutData->horizontalHeader(), &QHeaderView::customContextMenuRequested, + QObject::connect(ui->out1Table->horizontalHeader(), &QHeaderView::customContextMenuRequested, [this](const QPoint &pos) { meshHeaderMenu(MeshDataStage::VSOut, pos); }); - QObject::connect(ui->gsoutData->horizontalHeader(), &QHeaderView::customContextMenuRequested, + QObject::connect(ui->out2Table->horizontalHeader(), &QHeaderView::customContextMenuRequested, [this](const QPoint &pos) { meshHeaderMenu(MeshDataStage::GSOut, pos); }); QVBoxLayout *vertical = new QVBoxLayout(this); @@ -2634,7 +2835,22 @@ void BufferViewer::meshHeaderMenu(MeshDataStage stage, const QPoint &pos) m_CurFixed = false; m_ContextColumn = modelForStage(stage)->elementIndexForColumn(col); - m_SelectSecondAlphaColumn->setEnabled(modelForStage(stage)->elementForColumn(col).type.columns == 4); + bool perPrim = modelForStage(stage)->propForColumn(col).perprimitive; + + if(perPrim) + { + m_SelectPosColumn->setEnabled(false); + m_SelectSecondColumn->setEnabled(false); + m_SelectSecondAlphaColumn->setEnabled(false); + } + else + { + m_SelectPosColumn->setEnabled(true); + m_SelectSecondColumn->setEnabled(true); + + m_SelectSecondAlphaColumn->setEnabled(modelForStage(stage)->elementForColumn(col).type.columns == + 4); + } m_HeaderMenu->popup(tableForStage(stage)->horizontalHeader()->mapToGlobal(pos)); } @@ -2671,7 +2887,8 @@ void BufferViewer::fixedVars_contextMenu(const QPoint &pos) contextMenu.addSeparator(); - contextMenu.addAction(&showPadding); + if(!m_MeshView) + contextMenu.addAction(&showPadding); contextMenu.addSeparator(); @@ -2713,7 +2930,8 @@ void BufferViewer::stageRowMenu(MeshDataStage stage, QMenu *menu, const QPoint & { m_DebugVert->setToolTip(tr("This API does not support shader debugging")); } - else if(!m_Ctx.CurAction() || !(m_Ctx.CurAction()->flags & ActionFlags::Drawcall)) + else if(!m_Ctx.CurAction() || + !(m_Ctx.CurAction()->flags & (ActionFlags::Drawcall | ActionFlags::MeshDispatch))) { m_DebugVert->setToolTip(tr("No draw call selected")); } @@ -2747,6 +2965,10 @@ void BufferViewer::stageRowMenu(MeshDataStage stage, QMenu *menu, const QPoint & contextMenu = ContextMenu::MeshPreview_VSOutVertex; else if(stage == MeshDataStage::GSOut) contextMenu = ContextMenu::MeshPreview_GSOutVertex; + else if(stage == MeshDataStage::TaskOut) + contextMenu = ContextMenu::MeshPreview_TaskOutVertex; + else if(stage == MeshDataStage::MeshOut) + contextMenu = ContextMenu::MeshPreview_MeshOutVertex; QModelIndex idx = m_CurView->selectionModel()->currentIndex(); @@ -2813,13 +3035,13 @@ void BufferViewer::OnCaptureClosed() void BufferViewer::FillScrolls(PopulateBufferData *bufdata) { - bufdata->vsinHoriz = ui->vsinData->horizontalScrollBar()->value(); - bufdata->vsoutHoriz = ui->vsoutData->horizontalScrollBar()->value(); - bufdata->gsoutHoriz = ui->gsoutData->horizontalScrollBar()->value(); + bufdata->inHoriz = ui->inTable->horizontalScrollBar()->value(); + bufdata->out1Horiz = ui->out1Table->horizontalScrollBar()->value(); + bufdata->out2Horiz = ui->out2Table->horizontalScrollBar()->value(); - bufdata->vsinVert = ui->vsinData->indexAt(QPoint(0, 0)).row(); - bufdata->vsoutVert = ui->vsoutData->indexAt(QPoint(0, 0)).row(); - bufdata->gsoutVert = ui->gsoutData->indexAt(QPoint(0, 0)).row(); + bufdata->inVert = ui->inTable->indexAt(QPoint(0, 0)).row(); + bufdata->out1Vert = ui->out1Table->indexAt(QPoint(0, 0)).row(); + bufdata->out2Vert = ui->out2Table->indexAt(QPoint(0, 0)).row(); } void BufferViewer::OnEventChanged(uint32_t eventId) @@ -2831,13 +3053,13 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(m_Scrolls) { - bufdata->vsinHoriz = m_Scrolls->vsinHoriz; - bufdata->vsoutHoriz = m_Scrolls->vsoutHoriz; - bufdata->gsoutHoriz = m_Scrolls->gsoutHoriz; + bufdata->inHoriz = m_Scrolls->inHoriz; + bufdata->out1Horiz = m_Scrolls->out1Horiz; + bufdata->out2Horiz = m_Scrolls->out2Horiz; - bufdata->vsinVert = m_Scrolls->vsinVert; - bufdata->vsoutVert = m_Scrolls->vsoutVert; - bufdata->gsoutVert = m_Scrolls->gsoutVert; + bufdata->inVert = m_Scrolls->inVert; + bufdata->out1Vert = m_Scrolls->out1Vert; + bufdata->out2Vert = m_Scrolls->out2Vert; delete m_Scrolls; m_Scrolls = NULL; @@ -2854,15 +3076,17 @@ void BufferViewer::OnEventChanged(uint32_t eventId) m_Scroll[(int)MeshDataStage::VSOut] = QPoint(-1, -1); m_Scroll[(int)MeshDataStage::GSOut] = QPoint(-1, -1); - bufdata->highlightNames[0] = m_ModelVSIn->posName(); - bufdata->highlightNames[1] = m_ModelVSIn->secondaryName(); - bufdata->highlightNames[2] = m_ModelVSOut->posName(); - bufdata->highlightNames[3] = m_ModelVSOut->secondaryName(); - bufdata->highlightNames[4] = m_ModelGSOut->posName(); - bufdata->highlightNames[5] = m_ModelGSOut->secondaryName(); + bufdata->highlightNames[0] = m_ModelIn->posName(); + bufdata->highlightNames[1] = m_ModelIn->secondaryName(); + bufdata->highlightNames[2] = m_ModelOut1->posName(); + bufdata->highlightNames[3] = m_ModelOut1->secondaryName(); + bufdata->highlightNames[4] = m_ModelOut2->posName(); + bufdata->highlightNames[5] = m_ModelOut2->secondaryName(); const ActionDescription *action = m_Ctx.CurAction(); + bufdata->meshDispatch = action && (action->flags & ActionFlags::MeshDispatch); + configureDrawRange(); if(m_MeshView) @@ -2877,14 +3101,14 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(pipe.IsRestartEnabled() && action && (action->flags & ActionFlags::Indexed)) { - bufdata->vsinConfig.primRestart = pipe.GetRestartIndex(); + bufdata->inConfig.primRestart = pipe.GetRestartIndex(); if(pipe.GetIBuffer().byteStride == 1) - bufdata->vsinConfig.primRestart &= 0xff; + bufdata->inConfig.primRestart &= 0xff; else if(pipe.GetIBuffer().byteStride == 2) - bufdata->vsinConfig.primRestart &= 0xffff; + bufdata->inConfig.primRestart &= 0xffff; - bufdata->vsoutConfig.primRestart = bufdata->vsinConfig.primRestart; + bufdata->out1Config.primRestart = bufdata->inConfig.primRestart; // GS Out doesn't use primitive restart because it is post-expansion } @@ -2949,20 +3173,20 @@ void BufferViewer::OnEventChanged(uint32_t eventId) ParsedFormat parsed = BufferFormatter::ParseFormatString(m_Format, m_ByteSize, IsCBufferView()); - bufdata->vsinConfig.fixedVars = parsed.fixed; - bufdata->vsinConfig.packing = parsed.packing; + bufdata->inConfig.fixedVars = parsed.fixed; + bufdata->inConfig.packing = parsed.packing; if(parsed.repeating.type.baseType != VarType::Unknown) { - bufdata->vsinConfig.repeatStride = parsed.repeating.type.arrayByteStride; - bufdata->vsinConfig.repeatOffset = parsed.repeating.byteOffset; + bufdata->inConfig.repeatStride = parsed.repeating.type.arrayByteStride; + bufdata->inConfig.repeatOffset = parsed.repeating.byteOffset; - UnrollConstant(parsed.repeating, bufdata->vsinConfig.columns, bufdata->vsinConfig.props); + UnrollConstant(parsed.repeating, bufdata->inConfig.columns, bufdata->inConfig.props); } else { - bufdata->vsinConfig.repeatStride = 1U; - bufdata->vsinConfig.repeatOffset = parsed.fixed.type.arrayByteStride; + bufdata->inConfig.repeatStride = 1U; + bufdata->inConfig.repeatOffset = parsed.fixed.type.arrayByteStride; } if((m_Format.isEmpty() || !bufdata->cb.bytesBacked) && IsCBufferView()) @@ -2972,32 +3196,32 @@ void BufferViewer::OnEventChanged(uint32_t eventId) const ShaderReflection *reflection = m_Ctx.CurPipelineState().GetShaderReflection(m_CBufferSlot.stage); - bufdata->vsinConfig.fixedVars.type.members = + bufdata->inConfig.fixedVars.type.members = reflection->constantBlocks[m_CBufferSlot.slot].variables; if(IsD3D(m_Ctx.APIProps().pipelineType)) - bufdata->vsinConfig.packing = Packing::D3DCB; + bufdata->inConfig.packing = Packing::D3DCB; else - bufdata->vsinConfig.packing = BufferFormatter::EstimatePackingRules( - reflection->resourceId, bufdata->vsinConfig.fixedVars.type.members); + bufdata->inConfig.packing = BufferFormatter::EstimatePackingRules( + reflection->resourceId, bufdata->inConfig.fixedVars.type.members); } } ClearModels(); } - updateWindowTitle(); + updateLabelsAndLayout(); - bufdata->vsinConfig.curInstance = bufdata->vsoutConfig.curInstance = - bufdata->gsoutConfig.curInstance = m_Config.curInstance; - bufdata->vsinConfig.curView = bufdata->vsoutConfig.curView = bufdata->gsoutConfig.curView = + bufdata->inConfig.curInstance = bufdata->out1Config.curInstance = + bufdata->out2Config.curInstance = m_Config.curInstance; + bufdata->inConfig.curView = bufdata->out1Config.curView = bufdata->out2Config.curView = m_Config.curView; - m_ModelVSIn->beginReset(); - m_ModelVSOut->beginReset(); - m_ModelGSOut->beginReset(); + m_ModelIn->beginReset(); + m_ModelOut1->beginReset(); + m_ModelOut2->beginReset(); - bufdata->vsinConfig.baseVertex = action ? action->baseVertex : 0; + bufdata->inConfig.baseVertex = action ? action->baseVertex : 0; ui->formatSpecifier->setEnabled(!IsCBufferView() || bufdata->cb.bytesBacked); @@ -3023,7 +3247,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId) QPointer me(this); - m_Ctx.Replay().AsyncInvoke([this, me, bufdata](IReplayController *r) { + m_Ctx.Replay().AsyncInvoke([this, me, action, bufdata](IReplayController *r) { if(!me) return; @@ -3031,12 +3255,22 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(m_MeshView) { - bufdata->postVS = r->GetPostVSData(bufdata->vsinConfig.curInstance, - bufdata->vsinConfig.curView, MeshDataStage::VSOut); - bufdata->postGS = r->GetPostVSData(bufdata->vsinConfig.curInstance, - bufdata->vsinConfig.curView, MeshDataStage::GSOut); + if(bufdata->meshDispatch) + { + bufdata->postOut1 = r->GetPostVSData(0, bufdata->inConfig.curView, MeshDataStage::TaskOut); + bufdata->postOut2 = r->GetPostVSData(0, bufdata->inConfig.curView, MeshDataStage::MeshOut); - RT_FetchMeshData(r, m_Ctx, bufdata); + RT_FetchMeshPipeData(r, m_Ctx, bufdata); + } + else + { + bufdata->postOut1 = r->GetPostVSData(bufdata->inConfig.curInstance, + bufdata->inConfig.curView, MeshDataStage::VSOut); + bufdata->postOut2 = r->GetPostVSData(bufdata->inConfig.curInstance, + bufdata->inConfig.curView, MeshDataStage::GSOut); + + RT_FetchVertexPipeData(r, m_Ctx, bufdata); + } if(!me) return; @@ -3046,12 +3280,12 @@ void BufferViewer::OnEventChanged(uint32_t eventId) buf = new BufferData; // calculate tight stride - buf->stride = std::max(1U, bufdata->vsinConfig.repeatStride); + buf->stride = std::max(1U, bufdata->inConfig.repeatStride); // we want to fetch the data for fixed and repeated sections (either of which might be 0) // but calculate the number of rows etc for the repeated sections based on just the data // available for it - const uint64_t fixedLength = bufdata->vsinConfig.repeatOffset; + const uint64_t fixedLength = bufdata->inConfig.repeatOffset; // the "permanent" repeated range starts after the fixed data and goes for m_ByteSize uint64_t repeatedRangeStart = m_ByteOffset + fixedLength; @@ -3077,7 +3311,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId) repeatedRangeStart = qMin(repeatedRangeStart, bufferLength); // store the number of rows unclamped without the paging window - bufdata->vsinConfig.unclampedNumRows = + bufdata->inConfig.unclampedNumRows = uint32_t((repeatedRangeEnd - repeatedRangeStart + buf->stride - 1) / buf->stride); // advance the range by the paging offset @@ -3116,11 +3350,11 @@ void BufferViewer::OnEventChanged(uint32_t eventId) uint32_t repeatedDataAvailable = uint32_t(buf->size() - fixedLength); - bufdata->vsinConfig.pagingOffset = uint32_t(m_PagingByteOffset / buf->stride); - bufdata->vsinConfig.numRows = uint32_t((repeatedDataAvailable + buf->stride - 1) / buf->stride); + bufdata->inConfig.pagingOffset = uint32_t(m_PagingByteOffset / buf->stride); + bufdata->inConfig.numRows = uint32_t((repeatedDataAvailable + buf->stride - 1) / buf->stride); // ownership passes to model - bufdata->vsinConfig.buffers.push_back(buf); + bufdata->inConfig.buffers.push_back(buf); if(!me) { @@ -3135,7 +3369,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId) { // only fetch the cbuffer constants if this binding is currently valid if(bufdata->cb.valid) - bufdata->vsinConfig.evalVars = r->GetCBufferVariableContents( + bufdata->inConfig.evalVars = r->GetCBufferVariableContents( bufdata->cb.pipe, bufdata->cb.shader, m_CBufferSlot.stage, bufdata->cb.entryPoint, m_CBufferSlot.slot, m_BufferID, m_ByteOffset, m_ByteSize); } @@ -3144,87 +3378,85 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(bufdata->sequence != m_Sequence) return; - if(!bufdata->vsoutConfig.statusString.isEmpty()) + if(!bufdata->out1Config.statusString.isEmpty()) { - bufdata->vsoutConfig.columns.clear(); - bufdata->vsoutConfig.props.clear(); - ConfigureStatusColumn(bufdata->vsoutConfig.columns, bufdata->vsoutConfig.props); + bufdata->out1Config.columns.clear(); + bufdata->out1Config.props.clear(); + ConfigureStatusColumn(bufdata->out1Config.columns, bufdata->out1Config.props); } - if(!bufdata->gsoutConfig.statusString.isEmpty()) + if(!bufdata->out2Config.statusString.isEmpty()) { - bufdata->gsoutConfig.columns.clear(); - bufdata->gsoutConfig.props.clear(); - ConfigureStatusColumn(bufdata->gsoutConfig.columns, bufdata->gsoutConfig.props); + bufdata->out2Config.columns.clear(); + bufdata->out2Config.props.clear(); + ConfigureStatusColumn(bufdata->out2Config.columns, bufdata->out2Config.props); } - m_ModelVSIn->endReset(bufdata->vsinConfig); - m_ModelVSOut->endReset(bufdata->vsoutConfig); - m_ModelGSOut->endReset(bufdata->gsoutConfig); + m_ModelIn->endReset(bufdata->inConfig); + m_ModelOut1->endReset(bufdata->out1Config); + m_ModelOut2->endReset(bufdata->out2Config); - m_PostVS = bufdata->postVS; - m_PostGS = bufdata->postGS; + m_Out1Data = bufdata->postOut1; + m_Out2Data = bufdata->postOut2; m_CurCBuffer = bufdata->cb; // if we didn't have a position column selected before, or the name has changed, re-guess - if(m_ModelVSIn->posColumn() == -1 || - bufdata->highlightNames[0] != bufdata->vsinConfig.columnName(m_ModelVSIn->posColumn())) - m_ModelVSIn->setPosColumn(-1); + if(m_ModelIn->posColumn() == -1 || + bufdata->highlightNames[0] != bufdata->inConfig.columnName(m_ModelIn->posColumn())) + m_ModelIn->setPosColumn(-1); // similarly for secondary columns - if(m_ModelVSIn->secondaryColumn() == -1 || - bufdata->highlightNames[1] != bufdata->vsinConfig.columnName(m_ModelVSIn->secondaryColumn())) - m_ModelVSIn->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); + if(m_ModelIn->secondaryColumn() == -1 || + bufdata->highlightNames[1] != bufdata->inConfig.columnName(m_ModelIn->secondaryColumn())) + m_ModelIn->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); // and as above for VS Out / GS Out - if(m_ModelVSOut->posColumn() == -1 || - bufdata->highlightNames[2] != bufdata->vsoutConfig.columnName(m_ModelVSOut->posColumn())) - m_ModelVSOut->setPosColumn(-1); - if(m_ModelVSOut->secondaryColumn() == -1 || - bufdata->highlightNames[3] != - bufdata->vsoutConfig.columnName(m_ModelVSOut->secondaryColumn())) - m_ModelVSOut->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); + if(m_ModelOut1->posColumn() == -1 || + bufdata->highlightNames[2] != bufdata->out1Config.columnName(m_ModelOut1->posColumn())) + m_ModelOut1->setPosColumn(-1); + if(m_ModelOut1->secondaryColumn() == -1 || + bufdata->highlightNames[3] != bufdata->out1Config.columnName(m_ModelOut1->secondaryColumn())) + m_ModelOut1->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); - if(m_ModelGSOut->posColumn() == -1 || - bufdata->highlightNames[4] != bufdata->gsoutConfig.columnName(m_ModelGSOut->posColumn())) - m_ModelGSOut->setPosColumn(-1); - if(m_ModelGSOut->secondaryColumn() == -1 || - bufdata->highlightNames[5] != - bufdata->gsoutConfig.columnName(m_ModelGSOut->secondaryColumn())) - m_ModelGSOut->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); + if(m_ModelOut2->posColumn() == -1 || + bufdata->highlightNames[4] != bufdata->out2Config.columnName(m_ModelOut2->posColumn())) + m_ModelOut2->setPosColumn(-1); + if(m_ModelOut2->secondaryColumn() == -1 || + bufdata->highlightNames[5] != bufdata->out2Config.columnName(m_ModelOut2->secondaryColumn())) + m_ModelOut2->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); EnableCameraGuessControls(); populateBBox(bufdata); - UI_CalculateMeshFormats(); + UI_ConfigureFormats(); UpdateCurrentMeshConfig(); ApplyRowAndColumnDims( - m_ModelVSIn->columnCount(), ui->vsinData, - bufdata->vsinConfig.statusString.isEmpty() ? m_DataColWidth : m_ErrorColWidth); + m_ModelIn->columnCount(), ui->inTable, + bufdata->inConfig.statusString.isEmpty() ? m_DataColWidth : m_ErrorColWidth); ApplyRowAndColumnDims( - m_ModelVSOut->columnCount(), ui->vsoutData, - bufdata->vsoutConfig.statusString.isEmpty() ? m_DataColWidth : m_ErrorColWidth); + m_ModelOut1->columnCount(), ui->out1Table, + bufdata->out1Config.statusString.isEmpty() ? m_DataColWidth : m_ErrorColWidth); ApplyRowAndColumnDims( - m_ModelGSOut->columnCount(), ui->gsoutData, - bufdata->gsoutConfig.statusString.isEmpty() ? m_DataColWidth : m_ErrorColWidth); + m_ModelOut2->columnCount(), ui->out2Table, + bufdata->out2Config.statusString.isEmpty() ? m_DataColWidth : m_ErrorColWidth); - uint32_t numRows = qMax(qMax(bufdata->vsinConfig.numRows, bufdata->vsoutConfig.numRows), - bufdata->gsoutConfig.numRows); + uint32_t numRows = qMax(qMax(bufdata->inConfig.numRows, bufdata->out1Config.numRows), + bufdata->out2Config.numRows); if(!m_MeshView) - numRows = qMax(numRows, bufdata->vsinConfig.unclampedNumRows); + numRows = qMax(numRows, bufdata->inConfig.unclampedNumRows); ui->rowOffset->setMaximum((int)qMax(1U, numRows) - 1); - ScrollToRow(ui->vsinData, qMin(int(bufdata->vsinConfig.numRows) - 1, bufdata->vsinVert)); - ScrollToRow(ui->vsoutData, qMin(int(bufdata->vsoutConfig.numRows) - 1, bufdata->vsoutVert)); - ScrollToRow(ui->gsoutData, qMin(int(bufdata->gsoutConfig.numRows) - 1, bufdata->gsoutVert)); + ScrollToRow(ui->inTable, qMin(int(bufdata->inConfig.numRows) - 1, bufdata->inVert)); + ScrollToRow(ui->out1Table, qMin(int(bufdata->out1Config.numRows) - 1, bufdata->out1Vert)); + ScrollToRow(ui->out2Table, qMin(int(bufdata->out2Config.numRows) - 1, bufdata->out2Vert)); - ui->vsinData->horizontalScrollBar()->setValue(bufdata->vsinHoriz); - ui->vsoutData->horizontalScrollBar()->setValue(bufdata->vsoutHoriz); - ui->gsoutData->horizontalScrollBar()->setValue(bufdata->gsoutHoriz); + ui->inTable->horizontalScrollBar()->setValue(bufdata->inHoriz); + ui->out1Table->horizontalScrollBar()->setValue(bufdata->out1Horiz); + ui->out2Table->horizontalScrollBar()->setValue(bufdata->out2Horiz); for(MeshDataStage stage : {MeshDataStage::VSIn, MeshDataStage::VSOut, MeshDataStage::GSOut}) { @@ -3238,23 +3470,85 @@ void BufferViewer::OnEventChanged(uint32_t eventId) m_Scroll[i] = QPoint(-1, -1); } + if(m_MeshView) + { + RDTreeViewExpansionState state; + ui->fixedVars->saveExpansion(state, 0); + + ui->fixedVars->beginUpdate(); + + ui->fixedVars->clear(); + + if(bufdata->meshDispatch && !bufdata->out1Config.statusString.isEmpty()) + { + RDTreeWidgetItem *n = + new RDTreeWidgetItem({lit("-, -, -"), bufdata->out1Config.statusString, QString()}); + + ui->fixedVars->addTopLevelItem(n); + } + else if(bufdata->meshDispatch && !bufdata->out1Config.taskSizes.empty()) + { + const ActionDescription *action = m_Ctx.CurAction(); + + uint32_t i = 0; + for(uint32_t x = 0; x < action->dispatchDimension[0]; x++) + { + for(uint32_t y = 0; y < action->dispatchDimension[1]; y++) + { + for(uint32_t z = 0; z < action->dispatchDimension[2]; z++) + { + TaskGroupSize size = bufdata->out1Config.taskSizes[i]; + + RDTreeWidgetItem *n = new RDTreeWidgetItem( + {QFormatStr("%1, %2, %3").arg(x).arg(y).arg(z), + QFormatStr("Dispatched [%1, %2, %3]").arg(size.x).arg(size.y).arg(size.z), + lit("Task Group")}); + + if(!bufdata->out1Config.columns.empty()) + { + UI_AddTaskPayloads(n, i * bufdata->out1Config.buffers[0]->stride, + bufdata->out1Config.columns, bufdata->out1Config.buffers[0]); + } + + i++; + + ui->fixedVars->addTopLevelItem(n); + } + } + } + } + + ui->fixedVars->endUpdate(); + + ResourceId shader = m_Ctx.CurPipelineState().GetShader(ShaderStage::Task); + + // if we have saved expansion state for the new shader, apply it, otherwise apply the + // previous one to get any overlap (e.g. two different shaders with very similar or + // identical constants) + if(ui->fixedVars->hasInternalExpansion(qHash(ToQStr(shader)))) + ui->fixedVars->applyExpansion(ui->fixedVars->getInternalExpansion(qHash(ToQStr(shader))), + 0); + else + ui->fixedVars->applyExpansion(state, 0); + } + if(!m_MeshView) { m_RepeatedOffset->setText( - tr("Starting at: %1 bytes").arg(m_ByteOffset + bufdata->vsinConfig.repeatOffset)); + tr("Starting at: %1 bytes").arg(m_ByteOffset + bufdata->inConfig.repeatOffset)); { rdcarray vars; if((m_BufferID == ResourceId() && m_CurCBuffer.inlinedata.empty()) || m_Format.isEmpty()) { - vars = bufdata->vsinConfig.evalVars; + vars = bufdata->inConfig.evalVars; } else { - ShaderVariable var = InterpretShaderVar(bufdata->vsinConfig.fixedVars, - bufdata->vsinConfig.buffers[0]->storage.begin(), - bufdata->vsinConfig.buffers[0]->storage.end()); + ShaderVariable var = InterpretShaderVar(bufdata->inConfig.fixedVars, + bufdata->inConfig.buffers[0]->storage.begin(), + bufdata->inConfig.buffers[0]->storage.end()); vars.swap(var.members); } @@ -3271,7 +3565,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(!vars.isEmpty()) { UI_AddFixedVariables(ui->fixedVars->invisibleRootItem(), 0, - bufdata->vsinConfig.fixedVars.type.members, vars); + bufdata->inConfig.fixedVars.type.members, vars); if(IsCBufferView() && !bufdata->cb.bytesBacked) UI_RemoveOffsets(ui->fixedVars->invisibleRootItem()); @@ -3300,26 +3594,25 @@ void BufferViewer::OnEventChanged(uint32_t eventId) on_rowOffset_valueChanged(ui->rowOffset->value()); - const bool prev = (bufdata->vsinConfig.pagingOffset > 0); - const bool next = (bufdata->vsinConfig.numRows >= MaxVisibleRows); + const bool prev = (bufdata->inConfig.pagingOffset > 0); + const bool next = (bufdata->inConfig.numRows >= MaxVisibleRows); if(prev && next) { - ui->vsinData->setIndexWidget(m_ModelVSIn->index(0, 0), MakePreviousPageButton()); - ui->vsinData->setIndexWidget(m_ModelVSIn->index(0, 1), MakeNextPageButton()); + ui->inTable->setIndexWidget(m_ModelIn->index(0, 0), MakePreviousPageButton()); + ui->inTable->setIndexWidget(m_ModelIn->index(0, 1), MakeNextPageButton()); - ui->vsinData->setIndexWidget(m_ModelVSIn->index(MaxVisibleRows + 1, 0), - MakePreviousPageButton()); - ui->vsinData->setIndexWidget(m_ModelVSIn->index(MaxVisibleRows + 1, 1), - MakeNextPageButton()); + ui->inTable->setIndexWidget(m_ModelIn->index(MaxVisibleRows + 1, 0), + MakePreviousPageButton()); + ui->inTable->setIndexWidget(m_ModelIn->index(MaxVisibleRows + 1, 1), MakeNextPageButton()); } else if(prev) { - ui->vsinData->setIndexWidget(m_ModelVSIn->index(0, 0), MakePreviousPageButton()); + ui->inTable->setIndexWidget(m_ModelIn->index(0, 0), MakePreviousPageButton()); } else if(next) { - ui->vsinData->setIndexWidget(m_ModelVSIn->index(MaxVisibleRows, 1), MakeNextPageButton()); + ui->inTable->setIndexWidget(m_ModelIn->index(MaxVisibleRows, 1), MakeNextPageButton()); } } @@ -3360,9 +3653,9 @@ void BufferViewer::populateBBox(PopulateBufferData *bufdata) bbox->eventId = eventId; - bbox->input[0] = bufdata->vsinConfig; - bbox->input[1] = bufdata->vsoutConfig; - bbox->input[2] = bufdata->vsoutConfig; + bbox->input[0] = bufdata->inConfig; + bbox->input[1] = bufdata->out1Config; + bbox->input[2] = bufdata->out1Config; QPointer me(this); @@ -3480,6 +3773,88 @@ void BufferViewer::UI_FixedAddMatrixRows(RDTreeWidgetItem *n, const ShaderConsta } } +static void TaskAddMatrixRows(RDTreeWidgetItem *n, const ShaderConstant &c, const ShaderVariable &v) +{ + if(v.rows > 1) + { + uint32_t vecSize = VarTypeByteSize(v.type) * v.columns; + + if(v.ColMajor()) + vecSize = VarTypeByteSize(v.type) * v.rows; + + for(uint32_t r = 0; r < v.rows; r++) + { + n->addChild(new RDTreeWidgetItem( + {QFormatStr("%1.row%2").arg(v.name).arg(r), RowString(v, r), RowTypeString(v)})); + } + } +} + +void BufferViewer::UI_AddTaskPayloads(RDTreeWidgetItem *root, size_t baseOffset, + const rdcarray &consts, BufferData *buffer) +{ + uint32_t offset = 0; + + for(size_t idx = 0; idx < consts.size(); idx++) + { + const ShaderConstant &c = consts[idx]; + ShaderVariable v = InterpretShaderVar(c, buffer->data() + baseOffset + offset, buffer->end()); + + RDTreeWidgetItem *n = new RDTreeWidgetItem({v.name, VarString(v, c), TypeString(v, c)}); + + root->addChild(n); + + TaskAddMatrixRows(n, c, v); + + // if it's an array the value (v) will be expanded with one element in each of v.members, but + // the constant (c) will just have the type with a number of elements + if(c.type.elements > 1) + { + ShaderConstant noarray = c; + noarray.type.elements = 1; + + // calculate the tight scalar-packed advance, so we can detect padding + uint32_t elSize = BufferFormatter::GetVarAdvance(Packing::Scalar, noarray); + + for(uint32_t e = 0; e < v.members.size(); e++) + { + const uint32_t elOffset = (uint32_t)baseOffset + c.byteOffset + c.type.arrayByteStride * e; + + RDTreeWidgetItem *el = new RDTreeWidgetItem( + {v.members[e].name, VarString(v.members[e], c), TypeString(v.members[e], c)}); + + // if it's an array of structs we can recurse, just need to do the outer iteration here + // because v.members[...].members will be the actual struct members because of the expansion + if(c.type.baseType == VarType::Struct) + { + UI_AddTaskPayloads(el, elOffset, c.type.members, buffer); + } + else + { + // otherwise just expand by hand since there will be no more members in c.type.members for + // us to recurse with + TaskAddMatrixRows(el, c, v.members[e]); + } + + n->addChild(el); + + // don't count the padding in the last struct in an array of structs, it will be handled as + // padding after the array + if(c.type.baseType == VarType::Struct && e + 1 == v.members.size()) + break; + } + } + // for single structs, recurse + else if(v.type == VarType::Struct) + { + UI_AddTaskPayloads(n, c.byteOffset, c.type.members, buffer); + } + + // advance by the tight scalar-packed advance, so we can detect padding + offset += BufferFormatter::GetVarAdvance(Packing::Scalar, c); + } +} + void BufferViewer::UI_AddFixedVariables(RDTreeWidgetItem *root, uint32_t baseOffset, const rdcarray &consts, const rdcarray &vars) @@ -3818,11 +4193,21 @@ void BufferViewer::UI_ResetArcball() INVOKE_MEMFN(RT_UpdateAndDisplay); } -void BufferViewer::UI_CalculateMeshFormats() +void BufferViewer::UI_ConfigureFormats() { if(!m_MeshView) return; + const ActionDescription *action = m_Ctx.CurAction(); + + if(action && (action->flags & ActionFlags::MeshDispatch)) + UI_ConfigureMeshPipeFormats(); + else + UI_ConfigureVertexPipeFormats(); +} + +void BufferViewer::UI_ConfigureVertexPipeFormats() +{ const PipeState &pipe = m_Ctx.CurPipelineState(); rdcarray vbs = pipe.GetVBuffers(); @@ -3830,184 +4215,227 @@ void BufferViewer::UI_CalculateMeshFormats() if(action) { - m_VSInPosition = MeshFormat(); - m_VSInSecondary = MeshFormat(); + m_InPosition = MeshFormat(); + m_InSecondary = MeshFormat(); - m_VSInPosition.allowRestart = pipe.IsRestartEnabled() && (action->flags & ActionFlags::Indexed); - m_VSInPosition.restartIndex = pipe.GetRestartIndex(); + m_InPosition.allowRestart = pipe.IsRestartEnabled() && (action->flags & ActionFlags::Indexed); + m_InPosition.restartIndex = pipe.GetRestartIndex(); - const BufferConfiguration &vsinConfig = m_ModelVSIn->getConfig(); + const BufferConfiguration &vsinConfig = m_ModelIn->getConfig(); if(!vsinConfig.columns.empty()) { - int elIdx = m_ModelVSIn->posColumn(); + int elIdx = m_ModelIn->posColumn(); if(elIdx < 0 || elIdx >= vsinConfig.columns.count()) elIdx = 0; if(vsinConfig.unclampedNumRows > 0) - m_VSInPosition.numIndices = vsinConfig.numRows; + m_InPosition.numIndices = vsinConfig.numRows; else - m_VSInPosition.numIndices = action->numIndices; + m_InPosition.numIndices = action->numIndices; if((action->flags & ActionFlags::Instanced) && action->numInstances == 0) - m_VSInPosition.numIndices = 0; + m_InPosition.numIndices = 0; BoundVBuffer ib = pipe.GetIBuffer(); - m_VSInPosition.topology = pipe.GetPrimitiveTopology(); - m_VSInPosition.indexByteStride = ib.byteStride; - m_VSInPosition.baseVertex = action->baseVertex; - m_VSInPosition.indexResourceId = ib.resourceId; + m_InPosition.topology = pipe.GetPrimitiveTopology(); + m_InPosition.indexByteStride = ib.byteStride; + m_InPosition.baseVertex = action->baseVertex; + m_InPosition.indexResourceId = ib.resourceId; uint32_t drawIdxByteOffs = action->indexOffset * ib.byteStride; - m_VSInPosition.indexByteOffset = ib.byteOffset + drawIdxByteOffs; + m_InPosition.indexByteOffset = ib.byteOffset + drawIdxByteOffs; if(ib.byteSize >= ~0U) - m_VSInPosition.indexByteSize = ib.byteSize; + m_InPosition.indexByteSize = ib.byteSize; else if(drawIdxByteOffs > ib.byteSize) - m_VSInPosition.indexByteSize = 0; + m_InPosition.indexByteSize = 0; else - m_VSInPosition.indexByteSize = ib.byteSize - drawIdxByteOffs; + m_InPosition.indexByteSize = ib.byteSize - drawIdxByteOffs; - if((action->flags & ActionFlags::Indexed) && m_VSInPosition.indexByteStride == 0) - m_VSInPosition.indexByteStride = 4U; + if((action->flags & ActionFlags::Indexed) && m_InPosition.indexByteStride == 0) + m_InPosition.indexByteStride = 4U; { const ShaderConstant &el = vsinConfig.columns[elIdx]; const BufferElementProperties &prop = vsinConfig.props[elIdx]; - m_VSInPosition.instanced = prop.perinstance; - m_VSInPosition.instStepRate = prop.instancerate; + m_InPosition.instanced = prop.perinstance; + m_InPosition.instStepRate = prop.instancerate; if(prop.buffer < vbs.count() && !vsinConfig.genericsEnabled[elIdx]) { - m_VSInPosition.vertexResourceId = vbs[prop.buffer].resourceId; - m_VSInPosition.vertexByteStride = vbs[prop.buffer].byteStride; - m_VSInPosition.vertexByteOffset = vbs[prop.buffer].byteOffset + el.byteOffset + - action->vertexOffset * m_VSInPosition.vertexByteStride; - m_VSInPosition.vertexByteSize = vbs[prop.buffer].byteSize; + m_InPosition.vertexResourceId = vbs[prop.buffer].resourceId; + m_InPosition.vertexByteStride = vbs[prop.buffer].byteStride; + m_InPosition.vertexByteOffset = vbs[prop.buffer].byteOffset + el.byteOffset + + action->vertexOffset * m_InPosition.vertexByteStride; + m_InPosition.vertexByteSize = vbs[prop.buffer].byteSize; } else { - m_VSInPosition.vertexResourceId = ResourceId(); - m_VSInPosition.vertexByteStride = 0; - m_VSInPosition.vertexByteOffset = 0; + m_InPosition.vertexResourceId = ResourceId(); + m_InPosition.vertexByteStride = 0; + m_InPosition.vertexByteOffset = 0; } - m_VSInPosition.format = prop.format; + m_InPosition.format = prop.format; } - elIdx = m_ModelVSIn->secondaryColumn(); + elIdx = m_ModelIn->secondaryColumn(); if(elIdx >= 0 && elIdx < vsinConfig.columns.count()) { const ShaderConstant &el = vsinConfig.columns[elIdx]; const BufferElementProperties &prop = vsinConfig.props[elIdx]; - m_VSInSecondary.instanced = prop.perinstance; - m_VSInSecondary.instStepRate = prop.instancerate; + m_InSecondary.instanced = prop.perinstance; + m_InSecondary.instStepRate = prop.instancerate; if(prop.buffer < vbs.count() && !vsinConfig.genericsEnabled[elIdx]) { - m_VSInSecondary.vertexResourceId = vbs[prop.buffer].resourceId; - m_VSInSecondary.vertexByteStride = vbs[prop.buffer].byteStride; - m_VSInSecondary.vertexByteOffset = vbs[prop.buffer].byteOffset + el.byteOffset + - action->vertexOffset * m_VSInSecondary.vertexByteStride; - m_VSInSecondary.vertexByteSize = vbs[prop.buffer].byteSize; + m_InSecondary.vertexResourceId = vbs[prop.buffer].resourceId; + m_InSecondary.vertexByteStride = vbs[prop.buffer].byteStride; + m_InSecondary.vertexByteOffset = vbs[prop.buffer].byteOffset + el.byteOffset + + action->vertexOffset * m_InSecondary.vertexByteStride; + m_InSecondary.vertexByteSize = vbs[prop.buffer].byteSize; } else { - m_VSInSecondary.vertexResourceId = ResourceId(); - m_VSInSecondary.vertexByteStride = 0; - m_VSInSecondary.vertexByteOffset = 0; + m_InSecondary.vertexResourceId = ResourceId(); + m_InSecondary.vertexByteStride = 0; + m_InSecondary.vertexByteOffset = 0; } - m_VSInSecondary.format = prop.format; - m_VSInSecondary.showAlpha = m_ModelVSIn->secondaryAlpha(); + m_InSecondary.format = prop.format; + m_InSecondary.showAlpha = m_ModelIn->secondaryAlpha(); } } - const BufferConfiguration &vsoutConfig = m_ModelVSOut->getConfig(); + const BufferConfiguration &out1Config = m_ModelOut1->getConfig(); - m_PostVSPosition = MeshFormat(); - m_PostVSSecondary = MeshFormat(); + m_Out1Position = MeshFormat(); + m_Out1Secondary = MeshFormat(); - if(!vsoutConfig.columns.empty()) + if(!out1Config.columns.empty()) { - int elIdx = m_ModelVSOut->posColumn(); - if(elIdx < 0 || elIdx >= vsoutConfig.columns.count()) + int elIdx = m_ModelOut1->posColumn(); + if(elIdx < 0 || elIdx >= out1Config.columns.count()) elIdx = 0; - const ShaderConstant &el = vsoutConfig.columns[elIdx]; - const BufferElementProperties &prop = vsoutConfig.props[elIdx]; + const ShaderConstant &el = out1Config.columns[elIdx]; + const BufferElementProperties &prop = out1Config.props[elIdx]; - m_PostVSPosition = m_PostVS; - m_PostVSPosition.vertexByteOffset += el.byteOffset; - m_PostVSPosition.unproject = prop.systemValue == ShaderBuiltin::Position; - m_PostVSPosition.format.compCount = el.type.columns; + m_Out1Position = m_Out1Data; + m_Out1Position.vertexByteOffset += el.byteOffset; + m_Out1Position.unproject = prop.systemValue == ShaderBuiltin::Position; + m_Out1Position.format.compCount = el.type.columns; // if geometry/tessellation is enabled, don't unproject VS output data if(m_Ctx.CurPipelineState().GetShader(ShaderStage::Tess_Eval) != ResourceId() || m_Ctx.CurPipelineState().GetShader(ShaderStage::Geometry) != ResourceId()) - m_PostVSPosition.unproject = false; + m_Out1Position.unproject = false; - elIdx = m_ModelVSOut->secondaryColumn(); + elIdx = m_ModelOut1->secondaryColumn(); - if(elIdx >= 0 && elIdx < vsoutConfig.columns.count()) + if(elIdx >= 0 && elIdx < out1Config.columns.count()) { - m_PostVSSecondary = m_PostVS; - m_PostVSSecondary.vertexByteOffset += vsoutConfig.columns[elIdx].byteOffset; - m_PostVSSecondary.format = prop.format; - m_PostVSSecondary.showAlpha = m_ModelVSOut->secondaryAlpha(); + m_Out1Secondary = m_Out1Data; + m_Out1Secondary.vertexByteOffset += out1Config.columns[elIdx].byteOffset; + m_Out1Secondary.format = prop.format; + m_Out1Secondary.showAlpha = m_ModelOut1->secondaryAlpha(); } } - m_PostVSPosition.allowRestart = m_VSInPosition.allowRestart; - m_PostVSPosition.restartIndex = m_VSInPosition.restartIndex; + m_Out1Position.allowRestart = m_InPosition.allowRestart; + m_Out1Position.restartIndex = m_InPosition.restartIndex; - const BufferConfiguration &gsoutConfig = m_ModelGSOut->getConfig(); + const BufferConfiguration &out2Config = m_ModelOut2->getConfig(); - m_PostGSPosition = MeshFormat(); - m_PostGSSecondary = MeshFormat(); + m_Out2Position = MeshFormat(); + m_Out2Secondary = MeshFormat(); - if(!gsoutConfig.columns.empty()) + if(!out2Config.columns.empty()) { - int elIdx = m_ModelGSOut->posColumn(); - if(elIdx < 0 || elIdx >= gsoutConfig.columns.count()) + int elIdx = m_ModelOut2->posColumn(); + if(elIdx < 0 || elIdx >= out2Config.columns.count()) elIdx = 0; - const ShaderConstant &el = gsoutConfig.columns[elIdx]; - const BufferElementProperties &prop = gsoutConfig.props[elIdx]; + const ShaderConstant &el = out2Config.columns[elIdx]; + const BufferElementProperties &prop = out2Config.props[elIdx]; - m_PostGSPosition = m_PostGS; - m_PostGSPosition.vertexByteOffset += el.byteOffset; - m_PostGSPosition.unproject = prop.systemValue == ShaderBuiltin::Position; + m_Out2Position = m_Out2Data; + m_Out2Position.vertexByteOffset += el.byteOffset; + m_Out2Position.unproject = prop.systemValue == ShaderBuiltin::Position; - elIdx = m_ModelGSOut->secondaryColumn(); + elIdx = m_ModelOut2->secondaryColumn(); - if(elIdx >= 0 && elIdx < gsoutConfig.columns.count()) + if(elIdx >= 0 && elIdx < out2Config.columns.count()) { - m_PostGSSecondary = m_PostGS; - m_PostGSSecondary.vertexByteOffset += gsoutConfig.columns[elIdx].byteOffset; - m_PostGSSecondary.showAlpha = m_ModelGSOut->secondaryAlpha(); + m_Out2Secondary = m_Out2Data; + m_Out2Secondary.vertexByteOffset += out2Config.columns[elIdx].byteOffset; + m_Out2Secondary.showAlpha = m_ModelOut2->secondaryAlpha(); } } - m_PostGSPosition.allowRestart = false; + m_Out2Position.allowRestart = false; - m_PostGSPosition.indexByteStride = 0; + m_Out2Position.indexByteStride = 0; if(!(action->flags & ActionFlags::Indexed)) - m_PostVSPosition.indexByteStride = m_VSInPosition.indexByteStride = 0; + m_Out1Position.indexByteStride = m_InPosition.indexByteStride = 0; } else { - m_VSInPosition = MeshFormat(); - m_VSInSecondary = MeshFormat(); + m_InPosition = MeshFormat(); + m_InSecondary = MeshFormat(); - m_PostVSPosition = MeshFormat(); - m_PostVSSecondary = MeshFormat(); + m_Out1Position = MeshFormat(); + m_Out1Secondary = MeshFormat(); - m_PostGSPosition = MeshFormat(); - m_PostGSSecondary = MeshFormat(); + m_Out2Position = MeshFormat(); + m_Out2Secondary = MeshFormat(); + } +} + +void BufferViewer::UI_ConfigureMeshPipeFormats() +{ + const PipeState &pipe = m_Ctx.CurPipelineState(); + + const ActionDescription *action = m_Ctx.CurAction(); + + m_InPosition = MeshFormat(); + m_InSecondary = MeshFormat(); + + // out1 is task shaders, which do not have displayable data + m_Out1Position = MeshFormat(); + m_Out1Secondary = MeshFormat(); + + const BufferConfiguration &out2Config = m_ModelOut2->getConfig(); + + m_Out2Position = MeshFormat(); + m_Out2Secondary = MeshFormat(); + m_Out2Position.allowRestart = false; + + if(!out2Config.columns.empty()) + { + int elIdx = m_ModelOut2->posColumn(); + if(elIdx < 0 || elIdx >= out2Config.columns.count()) + elIdx = 0; + + const ShaderConstant &el = out2Config.columns[elIdx]; + const BufferElementProperties &prop = out2Config.props[elIdx]; + + m_Out2Position = m_Out2Data; + m_Out2Position.vertexByteOffset += el.byteOffset; + m_Out2Position.unproject = prop.systemValue == ShaderBuiltin::Position; + + elIdx = m_ModelOut2->secondaryColumn(); + + if(elIdx >= 0 && elIdx < out2Config.columns.count()) + { + m_Out2Secondary = m_Out2Data; + m_Out2Secondary.vertexByteOffset += out2Config.columns[elIdx].byteOffset; + m_Out2Secondary.showAlpha = m_ModelOut2->secondaryAlpha(); + } } } @@ -4084,27 +4512,32 @@ void BufferViewer::UpdateCurrentMeshConfig() switch(m_CurStage) { case MeshDataStage::VSIn: - m_Config.position = m_VSInPosition; - m_Config.second = m_VSInSecondary; + m_Config.position = m_InPosition; + m_Config.second = m_InSecondary; break; case MeshDataStage::VSOut: - m_Config.position = m_PostVSPosition; - m_Config.second = m_PostVSSecondary; + m_Config.position = m_Out1Position; + m_Config.second = m_Out1Secondary; break; case MeshDataStage::GSOut: - m_Config.position = m_PostGSPosition; - m_Config.second = m_PostGSSecondary; + case MeshDataStage::MeshOut: + m_Config.position = m_Out2Position; + m_Config.second = m_Out2Secondary; break; + case MeshDataStage::TaskOut: default: break; } camGuess_changed(0.0); + m_Config.showBBox = false; + + if(m_CurStage == MeshDataStage::TaskOut) + return; + BufferItemModel *model = currentBufferModel(); int stage = currentStageIndex(); - m_Config.showBBox = false; - if(model) { int posEl = model->posColumn(); @@ -4215,12 +4648,23 @@ void BufferViewer::ScrollToColumn(RDTableView *view, int column) void BufferViewer::ShowMeshData(MeshDataStage stage) { + const ActionDescription *action = m_Ctx.CurAction(); + if(action && (action->flags & ActionFlags::MeshDispatch) && stage == MeshDataStage::VSIn) + { + ToolWindowManager::raiseToolWindow(m_Containers[2]); + return; + } + if(stage == MeshDataStage::VSIn) - ToolWindowManager::raiseToolWindow(ui->vsinData); + ToolWindowManager::raiseToolWindow(m_Containers[0]); else if(stage == MeshDataStage::VSOut) - ToolWindowManager::raiseToolWindow(ui->vsoutData); + ToolWindowManager::raiseToolWindow(m_Containers[1]); else if(stage == MeshDataStage::GSOut) - ToolWindowManager::raiseToolWindow(ui->gsoutData); + ToolWindowManager::raiseToolWindow(m_Containers[2]); + else if(stage == MeshDataStage::TaskOut) + ToolWindowManager::raiseToolWindow(m_Containers[0]); + else if(stage == MeshDataStage::MeshOut) + ToolWindowManager::raiseToolWindow(m_Containers[1]); } void BufferViewer::SetCurrentInstance(int32_t instance) @@ -4245,6 +4689,10 @@ void BufferViewer::SetPreviewStage(MeshDataStage stage) ui->outputTabs->setCurrentIndex(1); else if(stage == MeshDataStage::GSOut) ui->outputTabs->setCurrentIndex(2); + else if(stage == MeshDataStage::TaskOut) + ui->outputTabs->setCurrentIndex(1); + else if(stage == MeshDataStage::MeshOut) + ui->outputTabs->setCurrentIndex(2); } } @@ -4260,7 +4708,7 @@ void BufferViewer::ViewBuffer(uint64_t byteOffset, uint64_t byteSize, ResourceId m_BufferID = id; m_TexSub = {0, 0, 0}; - updateWindowTitle(); + updateLabelsAndLayout(); BufferDescription *buf = m_Ctx.GetBuffer(id); if(buf) @@ -4307,7 +4755,7 @@ void BufferViewer::ViewCBuffer(const ShaderStage stage, uint32_t slot, uint32_t m_CBufferSlot = {stage, slot, idx}; m_TexSub = {0, 0, 0}; - updateWindowTitle(); + updateLabelsAndLayout(); m_ObjectByteSize = 0; m_PagingByteOffset = 0; @@ -4334,7 +4782,7 @@ void BufferViewer::ViewTexture(ResourceId id, const Subresource &sub, const rdcs m_BufferID = id; m_TexSub = sub; - updateWindowTitle(); + updateLabelsAndLayout(); TextureDescription *tex = m_Ctx.GetTexture(id); if(tex) @@ -4377,7 +4825,7 @@ bool BufferViewer::eventFilter(QObject *watched, QEvent *event) QString tooltip; - Packing::Rules pack = m_ModelVSIn->getConfig().packing; + Packing::Rules pack = m_ModelIn->getConfig().packing; if(tag.valid && tag.padding) { @@ -4448,19 +4896,19 @@ bool BufferViewer::eventFilter(QObject *watched, QEvent *event) } } } - else if(!m_MeshView && watched == ui->vsinData->viewport()) + else if(!m_MeshView && watched == ui->inTable->viewport()) { QModelIndex index = - ui->vsinData->indexAt(ui->vsinData->viewport()->mapFromGlobal(QCursor::pos())); + ui->inTable->indexAt(ui->inTable->viewport()->mapFromGlobal(QCursor::pos())); if(index.isValid()) { - const ShaderConstant &c = m_ModelVSIn->elementForColumn(index.column()); + const ShaderConstant &c = m_ModelIn->elementForColumn(index.column()); - QModelIndex rowidx = m_ModelVSIn->index(index.row(), 0, index.parent()); - int row = m_ModelVSIn->data(rowidx).toInt(); + QModelIndex rowidx = m_ModelIn->index(index.row(), 0, index.parent()); + int row = m_ModelIn->data(rowidx).toInt(); - size_t stride = m_ModelVSIn->getConfig().buffers[0]->stride; + size_t stride = m_ModelIn->getConfig().buffers[0]->stride; QString tooltip; @@ -4486,7 +4934,7 @@ bool BufferViewer::eventFilter(QObject *watched, QEvent *event) } } } - else if(!m_MeshView && watched == ui->vsinData->viewport()) + else if(!m_MeshView && watched == ui->inTable->viewport()) { if(event->type() == QEvent::MouseMove) { @@ -4495,10 +4943,10 @@ bool BufferViewer::eventFilter(QObject *watched, QEvent *event) QMouseEvent *mouseEvent = (QMouseEvent *)event; if(m_delegate->linkHover(mouseEvent, font(), - ui->vsinData->indexAt(mouseEvent->localPos().toPoint()))) - ui->vsinData->setCursor(QCursor(Qt::PointingHandCursor)); + ui->inTable->indexAt(mouseEvent->localPos().toPoint()))) + ui->inTable->setCursor(QCursor(Qt::PointingHandCursor)); else - ui->vsinData->unsetCursor(); + ui->inTable->unsetCursor(); return ret; } @@ -4507,9 +4955,87 @@ bool BufferViewer::eventFilter(QObject *watched, QEvent *event) return QObject::eventFilter(watched, event); } -void BufferViewer::updateWindowTitle() +void BufferViewer::updateLabelsAndLayout() { - if(!m_MeshView) + if(m_MeshView) + { + setWindowTitle(tr("Mesh Viewer")); + + if(m_Ctx.IsCaptureLoaded()) + { + GraphicsAPI pipeType = m_Ctx.APIProps().pipelineType; + + if(isMeshDraw()) + { + m_Containers[0]->layout()->addWidget(ui->out1Table); + m_Containers[0]->layout()->addWidget(ui->fixedVars); + m_Containers[1]->layout()->addWidget(ui->out2Table); + m_Containers[2]->layout()->addWidget(ui->inTable); + + ui->fixedVars->setVisible(true); + ui->out1Table->setVisible(false); + m_Containers[2]->setWindowTitle(tr("Mesh Input")); + m_Containers[0]->setWindowTitle(IsD3D(pipeType) ? tr("Amp. Out") : tr("Task Out")); + m_Containers[1]->setWindowTitle(tr("Mesh Output")); + + if(ui->outputTabs->indexOf(ui->out1Tab) == 1) + ui->outputTabs->removeTab(1); + ui->outputTabs->setTabText(0, tr("Mesh Input")); + ui->outputTabs->setTabText(1, tr("Mesh Out")); + + if(ui->solidShading->itemText(ui->solidShading->count() - 1) != tr("Meshlet")) + ui->solidShading->addItem(tr("Meshlet")); + ui->solidShading->adjustSize(); + } + else + { + m_Containers[0]->layout()->addWidget(ui->inTable); + m_Containers[0]->layout()->addWidget(ui->fixedVars); + m_Containers[1]->layout()->addWidget(ui->out1Table); + m_Containers[2]->layout()->addWidget(ui->out2Table); + + ui->fixedVars->setVisible(false); + ui->out1Table->setVisible(true); + m_Containers[0]->setWindowTitle(tr("VS Input")); + m_Containers[1]->setWindowTitle(tr("VS Output")); + m_Containers[2]->setWindowTitle(tr("GS/DS Output")); + + ui->outputTabs->setTabText(0, tr("VS In")); + if(ui->outputTabs->indexOf(ui->out1Tab) < 0) + ui->outputTabs->insertTab(1, ui->out1Tab, tr("VS Out")); + ui->outputTabs->setTabText(1, tr("VS Out")); + ui->outputTabs->setTabText(2, tr("GS/DS Out")); + + if(ui->solidShading->itemText(ui->solidShading->count() - 1) == tr("Meshlet")) + ui->solidShading->removeItem(ui->solidShading->count() - 1); + ui->solidShading->adjustSize(); + } + } + else + { + m_Containers[0]->layout()->addWidget(ui->inTable); + m_Containers[0]->layout()->addWidget(ui->fixedVars); + m_Containers[1]->layout()->addWidget(ui->out1Table); + m_Containers[2]->layout()->addWidget(ui->out2Table); + + ui->fixedVars->setVisible(false); + ui->out1Table->setVisible(true); + m_Containers[0]->setWindowTitle(tr("VS Input")); + m_Containers[1]->setWindowTitle(tr("VS Output")); + m_Containers[2]->setWindowTitle(tr("GS/DS Output")); + + ui->outputTabs->setTabText(0, tr("VS In")); + if(ui->outputTabs->indexOf(ui->out1Tab) < 0) + ui->outputTabs->insertTab(1, ui->out1Tab, tr("VS Out")); + ui->outputTabs->setTabText(1, tr("VS Out")); + ui->outputTabs->setTabText(2, tr("GS/DS Out")); + + if(ui->solidShading->itemText(ui->solidShading->count() - 1) == tr("Meshlet")) + ui->solidShading->removeItem(ui->solidShading->count() - 1); + ui->solidShading->adjustSize(); + } + } + else { if(IsCBufferView()) { @@ -4639,11 +5165,15 @@ QPushButton *BufferViewer::MakeNextPageButton() RDTableView *BufferViewer::tableForStage(MeshDataStage stage) { if(stage == MeshDataStage::VSIn) - return ui->vsinData; + return ui->inTable; else if(stage == MeshDataStage::VSOut) - return ui->vsoutData; + return ui->out1Table; else if(stage == MeshDataStage::GSOut) - return ui->gsoutData; + return ui->out2Table; + else if(stage == MeshDataStage::TaskOut) + return ui->out1Table; + else if(stage == MeshDataStage::MeshOut) + return ui->out2Table; return NULL; } @@ -4651,11 +5181,15 @@ RDTableView *BufferViewer::tableForStage(MeshDataStage stage) BufferItemModel *BufferViewer::modelForStage(MeshDataStage stage) { if(stage == MeshDataStage::VSIn) - return m_ModelVSIn; + return m_ModelIn; else if(stage == MeshDataStage::VSOut) - return m_ModelVSOut; + return m_ModelOut1; else if(stage == MeshDataStage::GSOut) - return m_ModelGSOut; + return m_ModelOut2; + else if(stage == MeshDataStage::TaskOut) + return m_ModelOut1; + else if(stage == MeshDataStage::MeshOut) + return m_ModelOut2; return NULL; } @@ -4663,7 +5197,6 @@ BufferItemModel *BufferViewer::modelForStage(MeshDataStage stage) bool BufferViewer::isCurrentRasterOut() { BufferItemModel *model = currentBufferModel(); - int stage = currentStageIndex(); // if geometry/tessellation is enabled, only the GS out stage is rasterized output if((m_Ctx.CurPipelineState().GetShader(ShaderStage::Tess_Eval) != ResourceId() || @@ -4671,6 +5204,10 @@ bool BufferViewer::isCurrentRasterOut() m_CurStage != MeshDataStage::GSOut) return false; + // task shader outputs are not rasterized by definition + if(m_CurStage == MeshDataStage::TaskOut) + return false; + if(model) { int posEl = model->posColumn(); @@ -4678,6 +5215,21 @@ bool BufferViewer::isCurrentRasterOut() { return model->getConfig().props[posEl].systemValue == ShaderBuiltin::Position; } + + // if the model isn't prepared yet then return a sensible default answer - if no tess/geom, + // vertex is the output. Otherwise geom is the output. For task/mesh then mesh is the output + if(model->getConfig().columns.empty()) + { + if(m_Ctx.CurPipelineState().GetShader(ShaderStage::Tess_Eval) != ResourceId() || + m_Ctx.CurPipelineState().GetShader(ShaderStage::Geometry) != ResourceId()) + return m_CurStage == MeshDataStage::GSOut; + else if(m_CurStage == MeshDataStage::MeshOut) + return true; + else if(m_Ctx.CurPipelineState().GetShader(ShaderStage::Tess_Eval) == ResourceId() && + m_Ctx.CurPipelineState().GetShader(ShaderStage::Geometry) == ResourceId() && + m_CurStage != MeshDataStage::VSOut) + return true; + } } return false; @@ -4691,10 +5243,21 @@ int BufferViewer::currentStageIndex() return 1; else if(m_CurStage == MeshDataStage::GSOut) return 2; + else if(m_CurStage == MeshDataStage::TaskOut) + return 1; + else if(m_CurStage == MeshDataStage::MeshOut) + return 2; return 0; } +bool BufferViewer::isMeshDraw() +{ + const ActionDescription *action = m_Ctx.CurAction(); + + return action && action->flags & ActionFlags::MeshDispatch; +} + void BufferViewer::Reset() { m_Output = NULL; @@ -4703,16 +5266,20 @@ void BufferViewer::Reset() ClearModels(); - ui->vsinData->setColumnWidths({40, 40}); - ui->vsoutData->setColumnWidths({40, 40}); - ui->gsoutData->setColumnWidths({40, 40}); + updateLabelsAndLayout(); + + ui->fixedVars->clear(); + + ui->inTable->setColumnWidths({40, 40}); + ui->out1Table->setColumnWidths({40, 40}); + ui->out2Table->setColumnWidths({40, 40}); m_BBoxes.clear(); } void BufferViewer::ClearModels() { - for(BufferItemModel *m : {m_ModelVSIn, m_ModelVSOut, m_ModelGSOut}) + for(BufferItemModel *m : {m_ModelIn, m_ModelOut1, m_ModelOut2}) { if(!m) continue; @@ -4820,15 +5387,15 @@ void BufferViewer::CalcColumnWidth(int maxNumRows) test->ui[1] = 0x12345678; test->ui[2] = 0xffffffff; - m_ModelVSIn->beginReset(); + m_ModelIn->beginReset(); - m_ModelVSIn->endReset(bufconfig); + m_ModelIn->endReset(bufconfig); // measure this data so we can use this as column widths - ui->vsinData->resizeColumnsToContents(); + ui->inTable->resizeColumnsToContents(); // index/element column - m_IdxColWidth = ui->vsinData->columnWidth(0); + m_IdxColWidth = ui->inTable->columnWidth(0); int col = 1; if(m_MeshView) @@ -4837,13 +5404,13 @@ void BufferViewer::CalcColumnWidth(int maxNumRows) m_DataColWidth = 10; for(int c = 0; c < 5; c++) { - int colWidth = ui->vsinData->columnWidth(col + c); + int colWidth = ui->inTable->columnWidth(col + c); m_DataColWidth = qMax(m_DataColWidth, colWidth); } - ui->vsinData->resizeRowsToContents(); + ui->inTable->resizeRowsToContents(); - m_DataRowHeight = ui->vsinData->rowHeight(0); + m_DataRowHeight = ui->inTable->rowHeight(0); } void BufferViewer::data_selected(const QItemSelection &selected, const QItemSelection &deselected) @@ -4911,13 +5478,23 @@ void BufferViewer::camGuess_changed(double value) if(m_CurStage == MeshDataStage::VSOut) { - m_Config.position.nearPlane = m_PostVS.nearPlane; - m_Config.position.flipY = m_PostVS.flipY; + m_Config.position.nearPlane = m_Out1Data.nearPlane; + m_Config.position.flipY = m_Out1Data.flipY; } else if(m_CurStage == MeshDataStage::GSOut) { - m_Config.position.nearPlane = m_PostGS.nearPlane; - m_Config.position.flipY = m_PostGS.flipY; + m_Config.position.nearPlane = m_Out2Data.nearPlane; + m_Config.position.flipY = m_Out2Data.flipY; + } + else if(m_CurStage == MeshDataStage::TaskOut) + { + m_Config.position.nearPlane = m_Out1Data.nearPlane; + m_Config.position.flipY = m_Out1Data.flipY; + } + else if(m_CurStage == MeshDataStage::MeshOut) + { + m_Config.position.nearPlane = m_Out2Data.nearPlane; + m_Config.position.flipY = m_Out2Data.flipY; } if(ui->nearGuess->value() > 0.0) @@ -4926,9 +5503,13 @@ void BufferViewer::camGuess_changed(double value) m_Config.position.farPlane = 100.0f; if(m_CurStage == MeshDataStage::VSOut) - m_Config.position.farPlane = m_PostVS.farPlane; + m_Config.position.farPlane = m_Out1Data.farPlane; else if(m_CurStage == MeshDataStage::GSOut) - m_Config.position.farPlane = m_PostGS.farPlane; + m_Config.position.farPlane = m_Out2Data.farPlane; + else if(m_CurStage == MeshDataStage::TaskOut) + m_Config.position.farPlane = m_Out1Data.farPlane; + else if(m_CurStage == MeshDataStage::MeshOut) + m_Config.position.farPlane = m_Out2Data.farPlane; if(ui->farGuess->value() > 0.0) m_Config.position.farPlane = ui->farGuess->value(); @@ -5074,7 +5655,7 @@ void BufferViewer::processFormat(const QString &format) m_OuterSplitter->replaceWidget(0, m_InnerSplitter); m_FixedGroup->layout()->addWidget(ui->fixedVars); - m_RepeatedGroup->layout()->addWidget(ui->vsinData); + m_RepeatedGroup->layout()->addWidget(ui->inTable); // row offset should be shown in the repeated control bar, but no separator line is needed ui->offsetLine->setVisible(false); @@ -5087,14 +5668,14 @@ void BufferViewer::processFormat(const QString &format) hbox->insertWidget(1, ui->rowOffset); } ui->fixedVars->setVisible(true); - ui->vsinData->setVisible(true); + ui->inTable->setVisible(true); ui->showPadding->setVisible(true); m_InnerSplitter->setVisible(true); if(m_CurView == NULL && !m_CurFixed) - m_CurView = ui->vsinData; + m_CurView = ui->inTable; } else if(fixedVars) { @@ -5107,7 +5688,7 @@ void BufferViewer::processFormat(const QString &format) ui->rowOffset->setVisible(false); ui->fixedVars->setVisible(true); - ui->vsinData->setVisible(false); + ui->inTable->setVisible(false); ui->showPadding->setVisible(true); @@ -5118,8 +5699,8 @@ void BufferViewer::processFormat(const QString &format) } else if(repeatedVars) { - if(m_OuterSplitter->widget(0) != ui->vsinData) - m_OuterSplitter->replaceWidget(0, ui->vsinData); + if(m_OuterSplitter->widget(0) != ui->inTable) + m_OuterSplitter->replaceWidget(0, ui->inTable); // row offset should be shown with the other controls ui->offsetLine->setVisible(true); @@ -5145,13 +5726,13 @@ void BufferViewer::processFormat(const QString &format) } ui->fixedVars->setVisible(false); - ui->vsinData->setVisible(true); + ui->inTable->setVisible(true); ui->showPadding->setVisible(false); m_InnerSplitter->setVisible(false); - m_CurView = ui->vsinData; + m_CurView = ui->inTable; m_CurFixed = false; } @@ -5249,7 +5830,7 @@ void BufferViewer::updateExportActionNames() else { // if only one type of data is visible, the export is unambiguous - if(!ui->vsinData->isVisible() || !ui->fixedVars->isVisible()) + if(!ui->inTable->isVisible() || !ui->fixedVars->isVisible()) { m_ExportCSV->setText(csv.arg(QString())); m_ExportBytes->setText(bytes.arg(QString())); @@ -5557,7 +6138,7 @@ void BufferViewer::exportData(const BufferExport ¶ms) { if(params.format == BufferExport::RawBytes) { - BufferItemModel *model = (BufferItemModel *)ui->vsinData->model(); + BufferItemModel *model = (BufferItemModel *)ui->inTable->model(); const BufferConfiguration &config = model->getConfig(); size_t byteSize = 0; @@ -5672,7 +6253,7 @@ void BufferViewer::SyncViews(RDTableView *primary, bool selection, bool scroll) if(!ui->syncViews->isChecked()) return; - RDTableView *views[] = {ui->vsinData, ui->vsoutData, ui->gsoutData}; + RDTableView *views[] = {ui->inTable, ui->out1Table, ui->out2Table}; int horizScrolls[ARRAY_COUNT(views)] = {0}; @@ -5753,7 +6334,7 @@ void BufferViewer::on_outputTabs_currentChanged(int index) if(index == 0) m_CurStage = MeshDataStage::VSIn; else if(index == 1) - m_CurStage = MeshDataStage::VSOut; + m_CurStage = isMeshDraw() ? MeshDataStage::MeshOut : MeshDataStage::VSOut; else if(index == 2) m_CurStage = MeshDataStage::GSOut; @@ -5836,15 +6417,15 @@ void BufferViewer::on_solidShading_currentIndexChanged(int index) m_Config.solidShadeMode = (SolidShade)qMax(0, index); - m_ModelVSIn->setSecondaryColumn(m_ModelVSIn->secondaryColumn(), + m_ModelIn->setSecondaryColumn(m_ModelIn->secondaryColumn(), + m_Config.solidShadeMode == SolidShade::Secondary, + m_ModelIn->secondaryAlpha()); + m_ModelOut1->setSecondaryColumn(m_ModelOut1->secondaryColumn(), m_Config.solidShadeMode == SolidShade::Secondary, - m_ModelVSIn->secondaryAlpha()); - m_ModelVSOut->setSecondaryColumn(m_ModelVSOut->secondaryColumn(), - m_Config.solidShadeMode == SolidShade::Secondary, - m_ModelVSOut->secondaryAlpha()); - m_ModelGSOut->setSecondaryColumn(m_ModelGSOut->secondaryColumn(), - m_Config.solidShadeMode == SolidShade::Secondary, - m_ModelGSOut->secondaryAlpha()); + m_ModelOut1->secondaryAlpha()); + m_ModelOut2->setSecondaryColumn(m_ModelOut2->secondaryColumn(), + m_Config.solidShadeMode == SolidShade::Secondary, + m_ModelOut2->secondaryAlpha()); INVOKE_MEMFN(RT_UpdateAndDisplay); } @@ -5911,12 +6492,12 @@ void BufferViewer::on_viewIndex_valueChanged(int value) void BufferViewer::on_rowOffset_valueChanged(int value) { - if(!m_MeshView && m_ModelVSIn->getConfig().unclampedNumRows > 0) + if(!m_MeshView && m_ModelIn->getConfig().unclampedNumRows > 0) { int page = value / MaxVisibleRows; value %= MaxVisibleRows; - uint64_t pageOffset = page * MaxVisibleRows * m_ModelVSIn->getConfig().buffers[0]->stride; + uint64_t pageOffset = page * MaxVisibleRows * m_ModelIn->getConfig().buffers[0]->stride; // account for the extra row at the top with previous/next buttons if(pageOffset > 0) @@ -5932,14 +6513,14 @@ void BufferViewer::on_rowOffset_valueChanged(int value) } } - ScrollToRow(ui->vsinData, value); - ScrollToRow(ui->vsoutData, value); - ScrollToRow(ui->gsoutData, value); + ScrollToRow(ui->inTable, value); + ScrollToRow(ui->out1Table, value); + ScrollToRow(ui->out2Table, value); // when we're paging and we select the first row, actually scroll up to include the previous/next // buttons. if(!m_MeshView && value == 1 && m_PagingByteOffset > 0) - ui->vsinData->verticalScrollBar()->setValue(0); + ui->inTable->verticalScrollBar()->setValue(0); } void BufferViewer::on_autofitCamera_clicked() @@ -5957,26 +6538,9 @@ void BufferViewer::on_autofitCamera_clicked() bbox = m_BBoxes[m_Ctx.CurEvent()]; } - BufferItemModel *model = NULL; + BufferItemModel *model = m_ModelIn; int stage = 0; - switch(m_CurStage) - { - case MeshDataStage::VSIn: - model = m_ModelVSIn; - stage = 0; - break; - case MeshDataStage::VSOut: - model = m_ModelVSOut; - stage = 1; - break; - case MeshDataStage::GSOut: - model = m_ModelGSOut; - stage = 2; - break; - default: break; - } - if(bbox.bounds[stage].Min.isEmpty()) return; diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index a4d3fd622..3f02c2d3b 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -169,7 +169,7 @@ private: IReplayOutput *m_Output; - void updateWindowTitle(); + void updateLabelsAndLayout(); void configureDrawRange(); @@ -183,12 +183,12 @@ private: MeshDataStage m_CurStage; // cached data from PostVS data - MeshFormat m_PostVS, m_PostGS; + MeshFormat m_Out1Data, m_Out2Data; // the configurations for 3D preview - MeshFormat m_VSInPosition, m_VSInSecondary; - MeshFormat m_PostVSPosition, m_PostVSSecondary; - MeshFormat m_PostGSPosition, m_PostGSSecondary; + MeshFormat m_InPosition, m_InSecondary; + MeshFormat m_Out1Position, m_Out1Secondary; + MeshFormat m_Out2Position, m_Out2Secondary; QMutex m_BBoxLock; QMap m_BBoxes; @@ -198,6 +198,8 @@ private: void UI_UpdateBoundingBox(const CalcBoundingBoxData &bbox); void UI_UpdateBoundingBoxLabels(int compCount = 0); + void UI_AddTaskPayloads(RDTreeWidgetItem *root, size_t baseOffset, + const rdcarray &consts, BufferData *buffer); void UI_AddFixedVariables(RDTreeWidgetItem *root, uint32_t baseOffset, const rdcarray &consts, const rdcarray &vars); @@ -242,9 +244,24 @@ private: bool m_MeshView; - BufferItemModel *m_ModelVSIn; - BufferItemModel *m_ModelVSOut; - BufferItemModel *m_ModelGSOut; + // for ease of reading, these stages are named as in, out1, and out2. Note however that this does + // NOT correspond to which table widgets these fill out. Since it is most common to look at VS In + // and VS Out for classic vertex pipeline draws, and Task Out and Mesh Out for mesh shader draws + // (and because mesh shader input visualisation is not natively available) we pair up VS In and + // Task Out on the same control, VS Out and Mesh Out on the same control, and GS/Tess Out and Mesh + // In on the same control. + + // the input stage + BufferItemModel *m_ModelIn; + // the first output stage (vertex, or task) + BufferItemModel *m_ModelOut1; + // the second output stage (geometry, or mesh) + BufferItemModel *m_ModelOut2; + + // the container widgets for each stage which are remapped depending on the draw type to one of + // the above. This may mean 1:1 for traditional draws or [0] being out1 (task out), [1] being out2 + // (mesh out) and [2] being in + QWidget *m_Containers[3] = {}; PopulateBufferData *m_Scrolls = NULL; @@ -300,6 +317,7 @@ private: BufferItemModel *currentBufferModel() { return modelForStage(m_CurStage); } bool isCurrentRasterOut(); int currentStageIndex(); + bool isMeshDraw(); void SetupMeshView(); void SetupRawView(); @@ -311,7 +329,9 @@ private: void ClearModels(); - void UI_CalculateMeshFormats(); + void UI_ConfigureFormats(); + void UI_ConfigureVertexPipeFormats(); + void UI_ConfigureMeshPipeFormats(); void UpdateCurrentMeshConfig(); void EnableCameraGuessControls(); diff --git a/qrenderdoc/Windows/BufferViewer.ui b/qrenderdoc/Windows/BufferViewer.ui index ff98a2c1e..29a9f7741 100644 --- a/qrenderdoc/Windows/BufferViewer.ui +++ b/qrenderdoc/Windows/BufferViewer.ui @@ -41,7 +41,7 @@ false - + 60 @@ -78,7 +78,7 @@ false - + 280 @@ -130,7 +130,7 @@ true - + VS In @@ -681,7 +681,7 @@ Enter 0.0 to use automatic/guessed value derived from data. - + VS Out @@ -703,7 +703,7 @@ Enter 0.0 to use automatic/guessed value derived from data. - + GS/DS Out @@ -726,7 +726,7 @@ Enter 0.0 to use automatic/guessed value derived from data. - + 500 diff --git a/renderdoc/data/glsl/glsl_globals.h b/renderdoc/data/glsl/glsl_globals.h index 00d67f89d..7ef861019 100644 --- a/renderdoc/data/glsl/glsl_globals.h +++ b/renderdoc/data/glsl/glsl_globals.h @@ -172,10 +172,16 @@ vec3 CalcCubeCoord(vec2 uv, int face) #endif +// first few match SolidShade enum #define MESHDISPLAY_SOLID 0x1 #define MESHDISPLAY_FACELIT 0x2 #define MESHDISPLAY_SECONDARY 0x3 -#define MESHDISPLAY_SECONDARY_ALPHA 0x4 +#define MESHDISPLAY_MESHLET 0x4 + +// extra values below +#define MESHDISPLAY_SECONDARY_ALPHA 0x5 + +#define MAX_NUM_MESHLETS (512 * 1024) #define TEXDISPLAY_TYPEMASK 0xF #define TEXDISPLAY_UINT_TEX 0x10 diff --git a/renderdoc/data/glsl/glsl_ubos.h b/renderdoc/data/glsl/glsl_ubos.h index f440decf7..ddee1b380 100644 --- a/renderdoc/data/glsl/glsl_ubos.h +++ b/renderdoc/data/glsl/glsl_ubos.h @@ -41,11 +41,24 @@ BINDING(0) uniform MeshUBOData uint rawoutput; uint flipY; vec2 padding; + uvec4 meshletColours[12]; } INST_NAME(Mesh); #endif // defined(MESH_UBO) || defined(__cplusplus) +#if defined(MESH_UBO) && defined(VULKAN) + +layout(binding = 1, std140) readonly buffer meshlet_data +{ + uint meshletCount; + uint meshletOffset; + uvec4 data[]; +} +meshlet; + +#endif // defined(MESH_UBO) + #if defined(CHECKER_UBO) || defined(__cplusplus) BINDING(0) uniform CheckerboardUBOData diff --git a/renderdoc/data/glsl/mesh.frag b/renderdoc/data/glsl/mesh.frag index c94d9f16f..4afd34702 100644 --- a/renderdoc/data/glsl/mesh.frag +++ b/renderdoc/data/glsl/mesh.frag @@ -46,7 +46,7 @@ void main(void) { int type = Mesh.displayFormat; - if(type == MESHDISPLAY_SECONDARY) + if(type == MESHDISPLAY_SECONDARY || type == MESHDISPLAY_MESHLET) { color_out = vec4(SECONDARY_NAME.xyz, 1); } diff --git a/renderdoc/data/glsl/mesh.vert b/renderdoc/data/glsl/mesh.vert index 265547a18..5d29c30f1 100644 --- a/renderdoc/data/glsl/mesh.vert +++ b/renderdoc/data/glsl/mesh.vert @@ -42,6 +42,69 @@ IO_LOCATION(1) in SECONDARY_TYPE vsin_secondary; IO_LOCATION(0) out vec4 vsout_secondary; IO_LOCATION(1) out vec4 vsout_norm; +#ifdef VULKAN + +uint getMeshletCountAt(uint m) +{ + uint vecIdx = m / 4; + + if((m % 4) == 0) + return meshlet.data[vecIdx].x; + else if((m % 4) == 1) + return meshlet.data[vecIdx].y; + else if((m % 4) == 2) + return meshlet.data[vecIdx].z; + else if((m % 4) == 3) + return meshlet.data[vecIdx].w; +} + +vec4 getMeshletColor() +{ + uint searchIdx = VERTEX_ID; + + // array of prefix summed counts accessible via getMeshletCountAt [x, x+y, x+y+z, ...] we do a + // binary search to find which meshlet this index corresponds to + + uint first = 0, last = meshlet.meshletCount - 1; + uint count = last - first; + + while(count > 0) + { + uint halfrange = count / 2; + uint mid = first + halfrange; + + if(searchIdx < getMeshletCountAt(mid)) + { + count = halfrange; + } + else + { + first = mid + 1; + count -= halfrange + 1; + } + } + + uint meshletIndex = first; + + if(VERTEX_ID < getMeshletCountAt(meshletIndex)) + { + meshletIndex += meshlet.meshletOffset; + meshletIndex %= 48; + uvec4 meshletColor = Mesh.meshletColours[meshletIndex / 4]; + if((meshletIndex % 4) == 0) + return unpackUnorm4x8(meshletColor.x); + else if((meshletIndex % 4) == 1) + return unpackUnorm4x8(meshletColor.y); + else if((meshletIndex % 4) == 2) + return unpackUnorm4x8(meshletColor.z); + else if((meshletIndex % 4) == 3) + return unpackUnorm4x8(meshletColor.w); + } + + return vec4(0, 0, 0, 1); +} +#endif + void main(void) { vec2 psprite[4] = @@ -65,6 +128,9 @@ void main(void) vsout_norm = vec4(0, 0, 1, 1); #ifdef VULKAN + if(Mesh.displayFormat == MESHDISPLAY_MESHLET) + vsout_secondary = getMeshletColor(); + // GL->VK conventions gl_Position.y = -gl_Position.y; if(Mesh.flipY == 1) diff --git a/renderdoc/data/hlsl/hlsl_cbuffers.h b/renderdoc/data/hlsl/hlsl_cbuffers.h index f6cf1d113..0e7d32d03 100644 --- a/renderdoc/data/hlsl/hlsl_cbuffers.h +++ b/renderdoc/data/hlsl/hlsl_cbuffers.h @@ -106,8 +106,15 @@ cbuffer MeshVertexCBuffer REG(b0) row_major float4x4 ModelViewProj; float2 SpriteSize; - uint homogenousInput; + uint vertMeshDisplayFormat; + + uint meshletOffset; + uint meshletCount; + uint padding1; + uint padding2; + + uint4 meshletColours[12]; }; cbuffer MeshGeometryCBuffer REG(b0) @@ -243,10 +250,16 @@ cbuffer DebugSampleOperation REG(b0) #define RESTYPE_DEPTH_STENCIL_MS 0x7 #define RESTYPE_TEX2D_MS 0x9 +// first few match SolidShade enum #define MESHDISPLAY_SOLID 0x1 #define MESHDISPLAY_FACELIT 0x2 #define MESHDISPLAY_SECONDARY 0x3 -#define MESHDISPLAY_SECONDARY_ALPHA 0x4 +#define MESHDISPLAY_MESHLET 0x4 + +// extra values below +#define MESHDISPLAY_SECONDARY_ALPHA 0x5 + +#define MAX_NUM_MESHLETS (512 * 1024) #define TEXDISPLAY_TYPEMASK 0xF #define TEXDISPLAY_NANS 0x0100 diff --git a/renderdoc/data/hlsl/mesh.hlsl b/renderdoc/data/hlsl/mesh.hlsl index 75c6a0934..9da7b5def 100644 --- a/renderdoc/data/hlsl/mesh.hlsl +++ b/renderdoc/data/hlsl/mesh.hlsl @@ -37,6 +37,79 @@ struct meshA2V float4 secondary : sec; }; +StructuredBuffer meshletSizesBuf : register(t0); + +float4 unpackUnorm4x8(uint value) +{ + uint4 shifted = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24); + return float4(shifted) / 255.0; +} + +uint getMeshletCountAt(uint m) +{ + uint vecIdx = m / 4; + + uint count = 0; + if((m % 4) == 0) + count = meshletSizesBuf[vecIdx].x; + else if((m % 4) == 1) + count = meshletSizesBuf[vecIdx].y; + else if((m % 4) == 2) + count = meshletSizesBuf[vecIdx].z; + else if((m % 4) == 3) + count = meshletSizesBuf[vecIdx].w; + + return count; +} + +float4 getMeshletColor(uint vid) +{ + uint searchIdx = vid; + + // array of prefix summed counts accessible via getMeshletCountAt [x, x+y, x+y+z, ...] we do a + // binary search to find which meshlet this index corresponds to + + uint first = 0, last = meshletCount - 1; + uint count = last - first; + + while(count > 0) + { + uint halfrange = count / 2; + uint mid = first + halfrange; + + if(searchIdx < getMeshletCountAt(mid)) + { + count = halfrange; + } + else + { + first = mid + 1; + count -= halfrange + 1; + } + } + + uint meshletIndex = first; + + float4 col = float4(0, 0, 0, 1); + + if(vid < getMeshletCountAt(meshletIndex)) + { + meshletIndex += meshletOffset; + meshletIndex %= 48; + uint4 meshletColor = meshletColours[meshletIndex / 4]; + if((meshletIndex % 4) == 0) + col = unpackUnorm4x8(meshletColor.x); + else if((meshletIndex % 4) == 1) + col = unpackUnorm4x8(meshletColor.y); + else if((meshletIndex % 4) == 2) + col = unpackUnorm4x8(meshletColor.z); + else if((meshletIndex % 4) == 3) + col = unpackUnorm4x8(meshletColor.w); + } + + return col; +} + meshV2F RENDERDOC_MeshVS(meshA2V IN, uint vid : SV_VertexID) { meshV2F OUT = (meshV2F)0; @@ -56,6 +129,9 @@ meshV2F RENDERDOC_MeshVS(meshA2V IN, uint vid : SV_VertexID) OUT.norm = float3(0, 0, 1); OUT.secondary = IN.secondary; + if(vertMeshDisplayFormat == MESHDISPLAY_MESHLET) + OUT.secondary = getMeshletColor(vid); + return OUT; } @@ -128,7 +204,7 @@ float4 RENDERDOC_MeshPS(meshV2F IN) { uint type = MeshDisplayFormat; - if(type == MESHDISPLAY_SECONDARY) + if(type == MESHDISPLAY_SECONDARY || type == MESHDISPLAY_MESHLET) return float4(IN.secondary.xyz, 1); else if(type == MESHDISPLAY_SECONDARY_ALPHA) return float4(IN.secondary.www, 1); diff --git a/renderdoc/driver/d3d11/d3d11_rendermesh.cpp b/renderdoc/driver/d3d11/d3d11_rendermesh.cpp index ddd2ef99f..1e6085f4a 100644 --- a/renderdoc/driver/d3d11/d3d11_rendermesh.cpp +++ b/renderdoc/driver/d3d11/d3d11_rendermesh.cpp @@ -267,6 +267,12 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const rdcarray &secon m_pImmediateContext->IASetPrimitiveTopology(topo); pixelData.MeshDisplayFormat = (int)cfg.solidShadeMode; + if(cfg.solidShadeMode == SolidShade::Meshlet) + { + RDCERR("D3D11 does not support mesh rendering"); + pixelData.MeshDisplayFormat = (int)SolidShade::Solid; + } + if(cfg.solidShadeMode == SolidShade::Secondary && cfg.second.showAlpha) pixelData.MeshDisplayFormat = MESHDISPLAY_SECONDARY_ALPHA; diff --git a/renderdoc/driver/d3d12/d3d12_debug.cpp b/renderdoc/driver/d3d12/d3d12_debug.cpp index 77aba239b..b5f0b77d8 100644 --- a/renderdoc/driver/d3d12/d3d12_debug.cpp +++ b/renderdoc/driver/d3d12/d3d12_debug.cpp @@ -275,6 +275,8 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) cbvParam(D3D12_SHADER_VISIBILITY_GEOMETRY, 0, 0), // 'push constant' CBV constParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 0, 4), + // meshlet sizes SRV + srvParam(D3D12_SHADER_VISIBILITY_VERTEX, 0, 0), }, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); @@ -308,6 +310,41 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) {}, "ps_5_0", &m_MeshPS); } + { + D3D12_RESOURCE_DESC meshletSizeBuf = {}; + meshletSizeBuf.Alignment = 0; + meshletSizeBuf.DepthOrArraySize = 1; + meshletSizeBuf.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + meshletSizeBuf.Flags = D3D12_RESOURCE_FLAG_NONE; + meshletSizeBuf.Format = DXGI_FORMAT_UNKNOWN; + meshletSizeBuf.Height = 1; + meshletSizeBuf.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + meshletSizeBuf.MipLevels = 1; + meshletSizeBuf.SampleDesc.Count = 1; + meshletSizeBuf.SampleDesc.Quality = 0; + meshletSizeBuf.Width = sizeof(uint32_t) * (4 + MAX_NUM_MESHLETS); + + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + hr = m_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &meshletSizeBuf, + D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + __uuidof(ID3D12Resource), (void **)&m_MeshletBuf); + m_pDevice->InternalRef(); + + if(FAILED(hr)) + { + RDCERR("Failed to create meshlet size buffer, HRESULT: %s", ToStr(hr).c_str()); + } + + if(m_MeshletBuf) + m_MeshletBuf->SetName(L"m_MeshletBuf"); + } + { rdcstr hlsl = GetEmbeddedResource(misc_hlsl); @@ -508,6 +545,7 @@ D3D12DebugManager::~D3D12DebugManager() SAFE_RELEASE(m_MeshGS); SAFE_RELEASE(m_MeshPS); SAFE_RELEASE(m_MeshRootSig); + SAFE_RELEASE(m_MeshletBuf); SAFE_RELEASE(m_ShaderDebugRootSig); SAFE_RELEASE(m_MathIntrinsicsPso); @@ -925,6 +963,39 @@ D3D12_GPU_VIRTUAL_ADDRESS D3D12DebugManager::UploadConstants(const void *data, s return ret; } +D3D12_GPU_VIRTUAL_ADDRESS D3D12DebugManager::UploadMeshletSizes(uint32_t meshletIndexOffset, + const rdcarray &sizes) +{ + D3D12_GPU_VIRTUAL_ADDRESS ret = m_MeshletBuf->GetGPUVirtualAddress(); + + if(sizes.empty()) + return ret; + + rdcarray data; + data.resize(sizes.size()); + uint32_t prefixCount = meshletIndexOffset; + for(size_t i = 0; i < data.size(); i++) + { + prefixCount += sizes[i].numVertices; + data[i] = prefixCount; + } + + if(m_CurMeshletOffset + data.byteSize() > m_MeshletBuf->GetDesc().Width) + m_CurMeshletOffset = 0; + + ret += m_CurMeshletOffset; + + // passing the unwrapped object here is immaterial as all we do is Map/Unmap, but it means we can + // call this function while capturing without worrying about serialising the map or deadlocking. + FillBuffer(Unwrap(m_MeshletBuf), (size_t)m_CurMeshletOffset, data.data(), data.byteSize()); + + m_CurMeshletOffset += data.byteSize(); + m_CurMeshletOffset = + AlignUp(m_CurMeshletOffset, (UINT64)D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + + return ret; +} + ID3D12GraphicsCommandListX *D3D12DebugManager::ResetDebugList() { m_DebugList->Reset(m_DebugAlloc, NULL); diff --git a/renderdoc/driver/d3d12/d3d12_debug.h b/renderdoc/driver/d3d12/d3d12_debug.h index 61bb89e95..9f0930cc8 100644 --- a/renderdoc/driver/d3d12/d3d12_debug.h +++ b/renderdoc/driver/d3d12/d3d12_debug.h @@ -156,6 +156,8 @@ public: ID3D12Resource *MakeCBuffer(UINT64 size); void FillBuffer(ID3D12Resource *buf, size_t offset, const void *data, size_t size); D3D12_GPU_VIRTUAL_ADDRESS UploadConstants(const void *data, size_t size); + D3D12_GPU_VIRTUAL_ADDRESS UploadMeshletSizes(uint32_t meshletIndexOffset, + const rdcarray &sizes); ID3D12RootSignature *GetMeshRootSig() { return m_MeshRootSig; } ID3D12RootSignature *GetShaderDebugRootSig() { return m_ShaderDebugRootSig; } @@ -244,6 +246,9 @@ private: ID3D12RootSignature *m_MeshRootSig = NULL; std::map m_CachedMeshPipelines; + ID3D12Resource *m_MeshletBuf = NULL; + UINT64 m_CurMeshletOffset = 0; + // Shader debugging resources ID3D12RootSignature *m_ShaderDebugRootSig = NULL; ID3D12PipelineState *m_MathIntrinsicsPso = NULL; diff --git a/renderdoc/driver/d3d12/d3d12_rendermesh.cpp b/renderdoc/driver/d3d12/d3d12_rendermesh.cpp index ae7683445..6b194acda 100644 --- a/renderdoc/driver/d3d12/d3d12_rendermesh.cpp +++ b/renderdoc/driver/d3d12/d3d12_rendermesh.cpp @@ -275,6 +275,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon vertexData.ModelViewProj = projMat.Mul(camMat.Mul(axisMapMat)); vertexData.SpriteSize = Vec2f(); vertexData.homogenousInput = cfg.position.unproject; + vertexData.vertMeshDisplayFormat = MESHDISPLAY_SOLID; MeshPixelCBuffer pixelData; @@ -305,9 +306,16 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon vertexData.ModelViewProj = projMat.Mul(camMat.Mul(guessProjInv)); } + memcpy(&vertexData.meshletColours[0].x, uniqueColors, sizeof(uniqueColors)); + RDCCOMPILE_ASSERT(sizeof(vertexData.meshletColours) == sizeof(uniqueColors), + "Unique colors array is wrongly sized"); + D3D12_GPU_VIRTUAL_ADDRESS vsCB = GetDebugManager()->UploadConstants(&vertexData, sizeof(vertexData)); + D3D12_GPU_VIRTUAL_ADDRESS meshletBuf = GetDebugManager()->UploadMeshletSizes( + cfg.position.meshletIndexOffset, cfg.position.meshletSizes); + if(!secondaryDraws.empty()) { D3D12MarkerRegion region(list, "Secondary draws"); @@ -329,6 +337,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetGraphicsRootSignature(rootSig); list->SetGraphicsRootConstantBufferView(0, vsCB); list->SetGraphicsRootConstantBufferView(1, vsCB); // geometry - dummy fill + list->SetGraphicsRootShaderResourceView(3, meshletBuf); } pixelData.MeshColour.x = fmt.meshColor.x; @@ -460,6 +469,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; break; case SolidShade::Secondary: pipe = cache.pipes[MeshDisplayPipelines::ePipe_Secondary]; break; + case SolidShade::Meshlet: pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; break; } pixelData.MeshDisplayFormat = (int)cfg.solidShadeMode; @@ -470,7 +480,20 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetPipelineState(pipe); list->SetGraphicsRootSignature(cache.rootsig); - list->SetGraphicsRootConstantBufferView(0, vsCB); + size_t numMeshlets = RDCMIN(cfg.position.meshletSizes.size(), (size_t)MAX_NUM_MESHLETS); + + if(cfg.solidShadeMode == SolidShade::Meshlet) + { + vertexData.meshletCount = (uint32_t)numMeshlets; + vertexData.meshletOffset = (uint32_t)cfg.position.meshletOffset; + + vertexData.vertMeshDisplayFormat = MESHDISPLAY_MESHLET; + } + + D3D12_GPU_VIRTUAL_ADDRESS vsCBSolid = + GetDebugManager()->UploadConstants(&vertexData, sizeof(vertexData)); + + list->SetGraphicsRootConstantBufferView(0, vsCBSolid); if(solidShadeMode == SolidShade::Lit) { @@ -484,6 +507,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon { list->SetGraphicsRootConstantBufferView(1, vsCB); // dummy fill for geometry } + list->SetGraphicsRootShaderResourceView(3, meshletBuf); Vec4f colour(0.8f, 0.8f, 0.0f, 1.0f); list->SetGraphicsRoot32BitConstants(2, 4, &pixelData, 0); @@ -530,6 +554,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetGraphicsRootConstantBufferView(0, vsCB); list->SetGraphicsRootConstantBufferView(1, vsCB); + list->SetGraphicsRootShaderResourceView(3, meshletBuf); pixelData.MeshColour.x = cfg.position.meshColor.x; pixelData.MeshColour.y = cfg.position.meshColor.y; @@ -625,6 +650,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetGraphicsRootConstantBufferView(0, vsCB); list->SetGraphicsRootConstantBufferView(1, vsCB); + list->SetGraphicsRootShaderResourceView(3, meshletBuf); list->DrawInstanced(24, 1, 0, 0); } @@ -652,6 +678,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetGraphicsRootConstantBufferView(0, vsCB); list->SetGraphicsRootConstantBufferView(1, vsCB); + list->SetGraphicsRootShaderResourceView(3, meshletBuf); pixelData.MeshColour = Vec3f(1.0f, 0.0f, 0.0f); list->SetGraphicsRoot32BitConstants(2, 4, &pixelData, 0); @@ -706,6 +733,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetGraphicsRootConstantBufferView(0, vsCB); list->SetGraphicsRootConstantBufferView(1, vsCB); + list->SetGraphicsRootShaderResourceView(3, meshletBuf); list->DrawInstanced(24, 1, 0, 0); } @@ -776,6 +804,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetGraphicsRootConstantBufferView( 0, GetDebugManager()->UploadConstants(&vertexData, sizeof(vertexData))); + list->SetGraphicsRootShaderResourceView(3, meshletBuf); list->SetPipelineState(cache.pipes[MeshDisplayPipelines::ePipe_Solid]); diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index 26ffc924e..6e8ace9b6 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -3989,13 +3989,19 @@ void VulkanReplay::OverlayRendering::Destroy(WrappedVulkan *driver) void VulkanReplay::MeshRendering::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool) { - CREATE_OBJECT(DescSetLayout, - {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL}}); + CREATE_OBJECT( + DescSetLayout, + { + {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT, NULL}, + }); CREATE_OBJECT(PipeLayout, DescSetLayout, 0); CREATE_OBJECT(DescSet, descriptorPool, DescSetLayout); UBO.Create(driver, driver->GetDev(), sizeof(MeshUBOData), 16, 0); + MeshletSSBO.Create(driver, driver->GetDev(), sizeof(uint32_t) * (4 + MAX_NUM_MESHLETS), 16, + GPUBuffer::eGPUBufferSSBO); BBoxVB.Create(driver, driver->GetDev(), sizeof(Vec4f) * 128, 16, GPUBuffer::eGPUBufferVBuffer); Vec4f TLN = Vec4f(-1.0f, 1.0f, 0.0f, 1.0f); // TopLeftNear, etc... @@ -4057,13 +4063,17 @@ void VulkanReplay::MeshRendering::Init(WrappedVulkan *driver, VkDescriptorPool d AxisFrustumVB.Unmap(); - VkDescriptorBufferInfo meshrender = {}; + VkDescriptorBufferInfo meshubo = {}; + VkDescriptorBufferInfo meshssbo = {}; - UBO.FillDescriptor(meshrender); + UBO.FillDescriptor(meshubo); + MeshletSSBO.FillDescriptor(meshssbo); VkWriteDescriptorSet writes[] = { {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(DescSet), 0, 0, 1, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, NULL, &meshrender, NULL}, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, NULL, &meshubo, NULL}, + {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(DescSet), 1, 0, 1, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, NULL, &meshssbo, NULL}, }; VkDevice dev = driver->GetDev(); @@ -4078,6 +4088,7 @@ void VulkanReplay::MeshRendering::Destroy(WrappedVulkan *driver) UBO.Destroy(); BBoxVB.Destroy(); + MeshletSSBO.Destroy(); AxisFrustumVB.Destroy(); driver->vkDestroyDescriptorSetLayout(driver->GetDev(), DescSetLayout, NULL); diff --git a/renderdoc/driver/vulkan/vk_rendermesh.cpp b/renderdoc/driver/vulkan/vk_rendermesh.cpp index b6cfd4bb9..7df16e401 100644 --- a/renderdoc/driver/vulkan/vk_rendermesh.cpp +++ b/renderdoc/driver/vulkan/vk_rendermesh.cpp @@ -526,6 +526,8 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco ModelViewProj = projMat.Mul(camMat.Mul(guessProjInv)); } + uint32_t dynOffs[2] = {}; + if(!secondaryDraws.empty()) { size_t mapsUsed = 0; @@ -537,8 +539,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(fmt.vertexResourceId != ResourceId()) { // TODO should move the color to a push constant so we don't have to map all the time - uint32_t uboOffs = 0; - MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -584,7 +585,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(secondaryCache.pipes[VKMeshDisplayPipelines::ePipe_WireDepth])); @@ -702,14 +703,14 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco case SolidShade::Secondary: pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_Secondary]; break; + case SolidShade::Meshlet: pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; break; } // can't support lit rendering without the pipeline - maybe geometry shader wasn't supported. if(solidShadeMode == SolidShade::Lit && pipe == VK_NULL_HANDLE) pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; - uint32_t uboOffs = 0; - MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -726,11 +727,42 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(solidShadeMode == SolidShade::Secondary && cfg.second.showAlpha) data->displayFormat = MESHDISPLAY_SECONDARY_ALPHA; + if(solidShadeMode == SolidShade::Meshlet) + { + size_t numMeshlets = RDCMIN(cfg.position.meshletSizes.size(), (size_t)MAX_NUM_MESHLETS); + + uint32_t *meshletCounts = (uint32_t *)m_MeshRender.MeshletSSBO.Map( + &dynOffs[1], AlignUp4(numMeshlets + 4) * sizeof(uint32_t)); + if(!data) + return; + + if(cfg.position.meshletSizes.size() > MAX_NUM_MESHLETS) + RDCWARN("Too many meshlets: %zu, only colouring first %zu of them", + cfg.position.meshletSizes.size(), (size_t)MAX_NUM_MESHLETS); + + meshletCounts[0] = (uint32_t)numMeshlets; + meshletCounts[1] = (uint32_t)cfg.position.meshletOffset; + meshletCounts += 4; + + uint32_t prefixCount = cfg.position.meshletIndexOffset; + for(size_t i = 0; i < numMeshlets; i++) + { + prefixCount += cfg.position.meshletSizes[i].numVertices; + meshletCounts[i] = prefixCount; + } + + memcpy(&data->meshletColours[0].x, uniqueColors, sizeof(uniqueColors)); + RDCCOMPILE_ASSERT(sizeof(data->meshletColours) == sizeof(uniqueColors), + "Unique colors array is wrongly sized"); + + m_MeshRender.MeshletSSBO.Unmap(); + } + m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(pipe)); @@ -764,8 +796,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco Vec4f wireCol = Vec4f(cfg.position.meshColor.x, cfg.position.meshColor.y, cfg.position.meshColor.z, 1.0f); - uint32_t uboOffs = 0; - MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -780,7 +811,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[VKMeshDisplayPipelines::ePipe_WireDepth])); @@ -858,8 +889,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.BBoxVB.buf), &vboffs); - uint32_t uboOffs = 0; - MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -874,7 +904,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[VKMeshDisplayPipelines::ePipe_WireDepth])); @@ -888,8 +918,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco VkDeviceSize vboffs = 0; vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.AxisFrustumVB.buf), &vboffs); - uint32_t uboOffs = 0; - MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -904,7 +933,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[VKMeshDisplayPipelines::ePipe_Wire])); @@ -912,7 +941,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdDraw(Unwrap(cmd), 2, 1, 0, 0); // poke the color (this would be a good candidate for a push constant) - data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -927,10 +956,10 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdDraw(Unwrap(cmd), 2, 1, 2, 0); - data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -945,7 +974,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdDraw(Unwrap(cmd), 2, 1, 4, 0); } @@ -955,8 +984,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco VkDeviceSize vboffs = sizeof(Vec4f) * 6; // skim the axis helpers vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(m_MeshRender.AxisFrustumVB.buf), &vboffs); - uint32_t uboOffs = 0; - MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; @@ -971,7 +999,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[VKMeshDisplayPipelines::ePipe_Wire])); @@ -1067,8 +1095,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco uniforms.homogenousInput = cfg.position.unproject; uniforms.pointSpriteSize = Vec2f(0.0f, 0.0f); - uint32_t uboOffs = 0; - MeshUBOData *ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + MeshUBOData *ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; *ubodata = uniforms; @@ -1076,7 +1103,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[VKMeshDisplayPipelines::ePipe_Solid])); @@ -1087,14 +1114,14 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco // Draw active primitive (red) uniforms.color = Vec4f(1.0f, 0.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) - ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); if(activePrim.size() >= primSize) { @@ -1115,14 +1142,14 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco // Draw adjacent primitives (green) uniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) - ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); if(adjacentPrimVertices.size() >= primSize && (adjacentPrimVertices.size() % primSize) == 0) { @@ -1151,14 +1178,14 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco // Draw active vertex (blue) uniforms.color = Vec4f(0.0f, 0.0f, 1.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) - ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); // vertices are drawn with tri strips helper.topology = Topology::TriangleStrip; @@ -1173,7 +1200,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(cache.pipes[VKMeshDisplayPipelines::ePipe_Solid])); @@ -1196,14 +1223,14 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco // Draw inactive vertices (green) uniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) - ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&uboOffs); + ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; *ubodata = uniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, - UnwrapPtr(m_MeshRender.DescSet), 1, &uboOffs); + UnwrapPtr(m_MeshRender.DescSet), 2, dynOffs); if(!inactiveVertices.empty()) { diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index db7ba4a40..2a12a342a 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -682,6 +682,7 @@ private: void Destroy(WrappedVulkan *driver); GPUBuffer UBO; + GPUBuffer MeshletSSBO; GPUBuffer BBoxVB; GPUBuffer AxisFrustumVB; diff --git a/renderdoc/driver/vulkan/vk_shader_cache.cpp b/renderdoc/driver/vulkan/vk_shader_cache.cpp index 52a700502..147aa08f5 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.cpp +++ b/renderdoc/driver/vulkan/vk_shader_cache.cpp @@ -301,7 +301,7 @@ VulkanShaderCache::VulkanShaderCache(WrappedVulkan *driver) GenerateGLSLShader(source, ShaderType::Vulkan, 430, defines), blob); // if we missed the inputHash, make a copy there too. - if(m_CacheShaders) + if(m_CacheShaders && blob) { m_ShaderCache[inputHash] = new rdcarray(*blob); m_ShaderCacheDirty = true; diff --git a/renderdoc/replay/replay_driver.cpp b/renderdoc/replay/replay_driver.cpp index 952e84aba..ec0ad94eb 100644 --- a/renderdoc/replay/replay_driver.cpp +++ b/renderdoc/replay/replay_driver.cpp @@ -1345,6 +1345,17 @@ const Vec4f colorRamp[22] = { Vec4f(1.000000f, 0.878431f, 1.000000f, 1.0f), Vec4f(1.000000f, 1.000000f, 1.000000f, 1.0f), }; +// unique colors generated from https://mokole.com/palette.html +const uint32_t uniqueColors[48] = { + 0xff00008b, 0xff32cd32, 0xff8fbc8f, 0xff8b008b, 0xffb03060, 0xffd2b48c, 0xff9932cc, 0xffff0000, + 0xffff8c00, 0xffffd700, 0xff00ff00, 0xff00ff7f, 0xff4169e1, 0xffe9967a, 0xffdc143c, 0xff00ffff, + 0xff00bfff, 0xff0000ff, 0xffa020f0, 0xffadff2f, 0xffff6347, 0xffda70d6, 0xffff00ff, 0xfff0e68c, + 0xffffff54, 0xff6495ed, 0xffdda0dd, 0xff90ee90, 0xff87ceeb, 0xffff1493, 0xff7fffd4, 0xffff69b4, + 0xff808080, 0xffc0c0c0, 0xff2f4f4f, 0xff556b2f, 0xff8b4513, 0xff6b8e23, 0xff2e8b57, 0xff8b0000, + 0xff483d8b, 0xff008000, 0xffb8860b, 0xff008b8b, 0xff4682b4, 0xffd2691e, 0xff9acd32, 0xffcd5c5c, + +}; + bytebuf GetDiscardPattern(DiscardType type, const ResourceFormat &fmt, uint32_t rowPitch, bool invert) { static const rdcliteral patterns[] = { diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 76153af25..2d40aed09 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -352,6 +352,7 @@ struct HighlightCache }; extern const Vec4f colorRamp[22]; +extern const uint32_t uniqueColors[48]; enum class DiscardType : int {