in renderdoc/driver/vulkan/vk_replay.cpp [1153:2172]
void VulkanReplay::SavePipelineState(uint32_t eventId)
{
if(!m_VulkanPipelineState)
return;
const VulkanRenderState &state = m_pDriver->m_RenderState;
VulkanCreationInfo &c = m_pDriver->m_CreationInfo;
VKPipe::State &ret = *m_VulkanPipelineState;
VulkanResourceManager *rm = m_pDriver->GetResourceManager();
VkMarkerRegion::Begin(StringFormat::Fmt("FetchShaderFeedback for %u", eventId));
FetchShaderFeedback(eventId);
VkMarkerRegion::End();
{
// reset the pipeline state, but keep the descriptor set arrays. This prevents needless
// reallocations, we'll ensure that descriptors are fully overwritten below.
rdcarray<VKPipe::DescriptorSet> graphicsDescriptors;
rdcarray<VKPipe::DescriptorSet> computeDescriptors;
ret.graphics.descriptorSets.swap(graphicsDescriptors);
ret.compute.descriptorSets.swap(computeDescriptors);
ret = VKPipe::State();
ret.graphics.descriptorSets.swap(graphicsDescriptors);
ret.compute.descriptorSets.swap(computeDescriptors);
}
ret.pushconsts.resize(state.pushConstSize);
memcpy(ret.pushconsts.data(), state.pushconsts, state.pushConstSize);
// General pipeline properties
ret.compute.pipelineResourceId = rm->GetUnreplacedOriginalID(state.compute.pipeline);
ret.graphics.pipelineResourceId = rm->GetUnreplacedOriginalID(state.graphics.pipeline);
if(state.compute.pipeline != ResourceId())
{
const VulkanCreationInfo::Pipeline &p = c.m_Pipeline[state.compute.pipeline];
ret.compute.pipelineComputeLayoutResourceId = rm->GetOriginalID(p.compLayout);
ret.compute.flags = p.flags;
VKPipe::Shader &stage = ret.computeShader;
int i = 5; // 5 is the CS idx (VS, TCS, TES, GS, FS, CS)
{
stage.resourceId = rm->GetUnreplacedOriginalID(p.shaders[i].module);
stage.entryPoint = p.shaders[i].entryPoint;
stage.stage = ShaderStage::Compute;
if(p.shaders[i].refl)
stage.reflection = p.shaders[i].refl;
stage.pushConstantRangeByteOffset = stage.pushConstantRangeByteSize = 0;
for(const VkPushConstantRange &pr : c.m_PipelineLayout[p.compLayout].pushRanges)
{
if(pr.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT)
{
stage.pushConstantRangeByteOffset = pr.offset;
stage.pushConstantRangeByteSize = pr.size;
break;
}
}
stage.requiredSubgroupSize = p.shaders[i].requiredSubgroupSize;
stage.specializationData.clear();
// set up the defaults
if(p.shaders[i].refl)
{
for(size_t cb = 0; cb < p.shaders[i].refl->constantBlocks.size(); cb++)
{
if(p.shaders[i].refl->constantBlocks[cb].compileConstants)
{
for(const ShaderConstant &sc : p.shaders[i].refl->constantBlocks[cb].variables)
{
stage.specializationData.resize_for_index(sc.byteOffset + sizeof(uint64_t));
memcpy(stage.specializationData.data() + sc.byteOffset, &sc.defaultValue,
sizeof(uint64_t));
}
break;
}
}
}
// apply any specializations
for(const SpecConstant &s : p.shaders[i].specialization)
{
int32_t idx = p.shaders[i].patchData->specIDs.indexOf(s.specID);
if(idx == -1)
{
RDCWARN("Couldn't find offset for spec ID %u", s.specID);
continue;
}
size_t offs = idx * sizeof(uint64_t);
stage.specializationData.resize_for_index(offs + sizeof(uint64_t));
memcpy(stage.specializationData.data() + offs, &s.value, s.dataSize);
}
if(p.shaders[i].patchData)
stage.specializationIds = p.shaders[i].patchData->specIDs;
}
}
else
{
ret.compute.pipelineComputeLayoutResourceId = ResourceId();
ret.compute.flags = 0;
ret.computeShader = VKPipe::Shader();
}
if(state.graphics.pipeline != ResourceId())
{
const VulkanCreationInfo::Pipeline &p = c.m_Pipeline[state.graphics.pipeline];
ret.graphics.pipelinePreRastLayoutResourceId = rm->GetOriginalID(p.vertLayout);
ret.graphics.pipelineFragmentLayoutResourceId = rm->GetOriginalID(p.fragLayout);
ret.graphics.flags = p.flags;
// Input Assembly
ret.inputAssembly.indexBuffer.resourceId = rm->GetOriginalID(state.ibuffer.buf);
ret.inputAssembly.indexBuffer.byteOffset = state.ibuffer.offs;
ret.inputAssembly.indexBuffer.byteStride = state.ibuffer.bytewidth;
ret.inputAssembly.primitiveRestartEnable = state.primRestartEnable != VK_FALSE;
ret.inputAssembly.topology =
MakePrimitiveTopology(state.primitiveTopology, state.patchControlPoints);
// Vertex Input
ret.vertexInput.attributes.resize(state.vertexAttributes.size());
for(size_t i = 0; i < state.vertexAttributes.size(); i++)
{
ret.vertexInput.attributes[i].location = state.vertexAttributes[i].location;
ret.vertexInput.attributes[i].binding = state.vertexAttributes[i].binding;
ret.vertexInput.attributes[i].byteOffset = state.vertexAttributes[i].offset;
ret.vertexInput.attributes[i].format = MakeResourceFormat(state.vertexAttributes[i].format);
}
ret.vertexInput.bindings.resize(state.vertexBindings.size());
for(const VkVertexInputBindingDescription2EXT &b : state.vertexBindings)
{
ret.vertexInput.bindings.resize_for_index(b.binding);
ret.vertexInput.bindings[b.binding].vertexBufferBinding = b.binding;
ret.vertexInput.bindings[b.binding].perInstance = b.inputRate == VK_VERTEX_INPUT_RATE_INSTANCE;
ret.vertexInput.bindings[b.binding].instanceDivisor = b.divisor;
}
ret.vertexInput.vertexBuffers.resize(state.vbuffers.size());
for(size_t i = 0; i < state.vbuffers.size(); i++)
{
ret.vertexInput.vertexBuffers[i].resourceId = rm->GetOriginalID(state.vbuffers[i].buf);
ret.vertexInput.vertexBuffers[i].byteOffset = state.vbuffers[i].offs;
ret.vertexInput.vertexBuffers[i].byteStride = (uint32_t)state.vbuffers[i].stride;
ret.vertexInput.vertexBuffers[i].byteSize = (uint32_t)state.vbuffers[i].size;
}
// Shader Stages
VKPipe::Shader *stages[] = {
&ret.vertexShader,
&ret.tessControlShader,
&ret.tessEvalShader,
&ret.geometryShader,
&ret.fragmentShader,
// compute
NULL,
&ret.taskShader,
&ret.meshShader,
};
for(size_t i = 0; i < ARRAY_COUNT(stages); i++)
{
if(stages[i] == NULL)
continue;
stages[i]->resourceId = rm->GetUnreplacedOriginalID(p.shaders[i].module);
stages[i]->entryPoint = p.shaders[i].entryPoint;
stages[i]->stage = StageFromIndex(i);
if(p.shaders[i].refl)
stages[i]->reflection = p.shaders[i].refl;
stages[i]->pushConstantRangeByteOffset = stages[i]->pushConstantRangeByteSize = 0;
// don't have to handle separate vert/frag layouts as push constant ranges must be identical
for(const VkPushConstantRange &pr : c.m_PipelineLayout[p.vertLayout].pushRanges)
{
if(pr.stageFlags & ShaderMaskFromIndex(i))
{
stages[i]->pushConstantRangeByteOffset = pr.offset;
stages[i]->pushConstantRangeByteSize = pr.size;
break;
}
}
stages[i]->specializationData.clear();
stages[i]->requiredSubgroupSize = p.shaders[i].requiredSubgroupSize;
// set up the defaults
if(p.shaders[i].refl)
{
for(size_t cb = 0; cb < p.shaders[i].refl->constantBlocks.size(); cb++)
{
if(p.shaders[i].refl->constantBlocks[cb].compileConstants)
{
for(const ShaderConstant &sc : p.shaders[i].refl->constantBlocks[cb].variables)
{
stages[i]->specializationData.resize_for_index(sc.byteOffset + sizeof(uint64_t));
memcpy(stages[i]->specializationData.data() + sc.byteOffset, &sc.defaultValue,
sizeof(uint64_t));
}
break;
}
}
}
// apply any specializations
for(const SpecConstant &s : p.shaders[i].specialization)
{
int32_t idx = p.shaders[i].patchData->specIDs.indexOf(s.specID);
if(idx == -1)
{
RDCWARN("Couldn't find offset for spec ID %u", s.specID);
continue;
}
size_t offs = idx * sizeof(uint64_t);
stages[i]->specializationData.resize_for_index(offs + sizeof(uint64_t));
memcpy(stages[i]->specializationData.data() + offs, &s.value, s.dataSize);
}
if(p.shaders[i].patchData)
stages[i]->specializationIds = p.shaders[i].patchData->specIDs;
}
// Tessellation
ret.tessellation.numControlPoints = p.patchControlPoints;
ret.tessellation.domainOriginUpperLeft =
state.domainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT;
ret.transformFeedback.rasterizedStream = state.rasterStream;
// Transform feedback
ret.transformFeedback.buffers.resize(state.xfbbuffers.size());
for(size_t i = 0; i < state.xfbbuffers.size(); i++)
{
ret.transformFeedback.buffers[i].bufferResourceId = rm->GetOriginalID(state.xfbbuffers[i].buf);
ret.transformFeedback.buffers[i].byteOffset = state.xfbbuffers[i].offs;
ret.transformFeedback.buffers[i].byteSize = state.xfbbuffers[i].size;
ret.transformFeedback.buffers[i].active = false;
ret.transformFeedback.buffers[i].counterBufferResourceId = ResourceId();
ret.transformFeedback.buffers[i].counterBufferOffset = 0;
if(i >= state.firstxfbcounter)
{
size_t xfb = i - state.firstxfbcounter;
if(xfb < state.xfbcounters.size())
{
ret.transformFeedback.buffers[i].active = true;
ret.transformFeedback.buffers[i].counterBufferResourceId =
rm->GetOriginalID(state.xfbcounters[xfb].buf);
ret.transformFeedback.buffers[i].counterBufferOffset = state.xfbcounters[xfb].offs;
}
}
}
// Viewport/Scissors
size_t numViewScissors = state.views.size();
ret.viewportScissor.viewportScissors.resize(numViewScissors);
for(size_t i = 0; i < numViewScissors; i++)
{
if(i < state.views.size())
{
ret.viewportScissor.viewportScissors[i].vp.x = state.views[i].x;
ret.viewportScissor.viewportScissors[i].vp.y = state.views[i].y;
ret.viewportScissor.viewportScissors[i].vp.width = state.views[i].width;
ret.viewportScissor.viewportScissors[i].vp.height = state.views[i].height;
ret.viewportScissor.viewportScissors[i].vp.minDepth = state.views[i].minDepth;
ret.viewportScissor.viewportScissors[i].vp.maxDepth = state.views[i].maxDepth;
}
else
{
RDCEraseEl(ret.viewportScissor.viewportScissors[i].vp);
}
if(i < state.scissors.size())
{
ret.viewportScissor.viewportScissors[i].scissor.x = state.scissors[i].offset.x;
ret.viewportScissor.viewportScissors[i].scissor.y = state.scissors[i].offset.y;
ret.viewportScissor.viewportScissors[i].scissor.width = state.scissors[i].extent.width;
ret.viewportScissor.viewportScissors[i].scissor.height = state.scissors[i].extent.height;
}
else
{
RDCEraseEl(ret.viewportScissor.viewportScissors[i].scissor);
}
}
{
ret.viewportScissor.discardRectangles.resize(p.discardRectangles.size());
for(size_t i = 0; i < p.discardRectangles.size() && i < state.discardRectangles.size(); i++)
{
ret.viewportScissor.discardRectangles[i].x = state.discardRectangles[i].offset.x;
ret.viewportScissor.discardRectangles[i].y = state.discardRectangles[i].offset.y;
ret.viewportScissor.discardRectangles[i].width = state.discardRectangles[i].extent.width;
ret.viewportScissor.discardRectangles[i].height = state.discardRectangles[i].extent.height;
}
ret.viewportScissor.discardRectanglesExclusive =
(p.discardMode == VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT);
}
{
ret.viewportScissor.depthNegativeOneToOne = state.negativeOneToOne != VK_FALSE;
}
// Rasterizer
ret.rasterizer.depthClampEnable = state.depthClampEnable != VK_FALSE;
ret.rasterizer.depthClipEnable = state.depthClipEnable != VK_FALSE;
ret.rasterizer.rasterizerDiscardEnable = state.rastDiscardEnable != VK_FALSE;
ret.rasterizer.frontCCW = state.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE;
ret.rasterizer.conservativeRasterization = ConservativeRaster::Disabled;
switch(state.conservativeRastMode)
{
case VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT:
ret.rasterizer.conservativeRasterization = ConservativeRaster::Underestimate;
break;
case VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT:
ret.rasterizer.conservativeRasterization = ConservativeRaster::Overestimate;
break;
default: break;
}
ret.rasterizer.pipelineShadingRate = {state.pipelineShadingRate.width,
state.pipelineShadingRate.height};
ShadingRateCombiner combiners[2] = {};
for(int i = 0; i < 2; i++)
{
switch(state.shadingRateCombiners[i])
{
default:
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR:
combiners[i] = ShadingRateCombiner::Keep;
break;
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR:
combiners[i] = ShadingRateCombiner::Replace;
break;
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_KHR:
combiners[i] = ShadingRateCombiner::Min;
break;
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR:
combiners[i] = ShadingRateCombiner::Max;
break;
case VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR:
combiners[i] = ShadingRateCombiner::Multiply;
break;
}
}
ret.rasterizer.shadingRateCombiners = {combiners[0], combiners[1]};
ret.rasterizer.lineRasterMode = LineRaster::Default;
// "VK_LINE_RASTERIZATION_MODE_DEFAULT_HKR is equivalent to
// VK_LINE_RASTERIZATION_MODE_RECTANGULAR_KHR if VkPhysicalDeviceLimits::strictLines is VK_TRUE"
if(m_pDriver->GetDeviceProps().limits.strictLines)
ret.rasterizer.lineRasterMode = LineRaster::Rectangular;
switch(state.lineRasterMode)
{
case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_KHR:
ret.rasterizer.lineRasterMode = LineRaster::Rectangular;
break;
case VK_LINE_RASTERIZATION_MODE_BRESENHAM_KHR:
ret.rasterizer.lineRasterMode = LineRaster::Bresenham;
break;
case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_KHR:
ret.rasterizer.lineRasterMode = LineRaster::RectangularSmooth;
break;
default: break;
}
ret.rasterizer.lineStippleFactor = 0; // stippled line disable
ret.rasterizer.lineStipplePattern = 0;
if(state.stippledLineEnable)
{
ret.rasterizer.lineStippleFactor = state.stippleFactor;
ret.rasterizer.lineStipplePattern = state.stipplePattern;
}
ret.rasterizer.extraPrimitiveOverestimationSize = state.primOverestimationSize;
switch(state.polygonMode)
{
case VK_POLYGON_MODE_POINT: ret.rasterizer.fillMode = FillMode::Point; break;
case VK_POLYGON_MODE_LINE: ret.rasterizer.fillMode = FillMode::Wireframe; break;
case VK_POLYGON_MODE_FILL: ret.rasterizer.fillMode = FillMode::Solid; break;
default:
ret.rasterizer.fillMode = FillMode::Solid;
RDCERR("Unexpected value for FillMode %x", state.polygonMode);
break;
}
switch(state.cullMode)
{
case VK_CULL_MODE_NONE: ret.rasterizer.cullMode = CullMode::NoCull; break;
case VK_CULL_MODE_FRONT_BIT: ret.rasterizer.cullMode = CullMode::Front; break;
case VK_CULL_MODE_BACK_BIT: ret.rasterizer.cullMode = CullMode::Back; break;
case VK_CULL_MODE_FRONT_AND_BACK: ret.rasterizer.cullMode = CullMode::FrontAndBack; break;
default:
ret.rasterizer.cullMode = CullMode::NoCull;
RDCERR("Unexpected value for CullMode %x", state.cullMode);
break;
}
ret.rasterizer.provokingVertexFirst =
state.provokingVertexMode == VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT;
ret.rasterizer.depthBiasEnable = state.depthBiasEnable != VK_FALSE;
ret.rasterizer.depthBias = state.bias.depth;
ret.rasterizer.depthBiasClamp = state.bias.biasclamp;
ret.rasterizer.slopeScaledDepthBias = state.bias.slope;
ret.rasterizer.lineWidth = state.lineWidth;
// MSAA
ret.multisample.rasterSamples = state.rastSamples;
ret.multisample.sampleShadingEnable = p.sampleShadingEnable;
ret.multisample.minSampleShading = p.minSampleShading;
ret.multisample.sampleMask = state.sampleMask[0];
ret.multisample.sampleLocations.customLocations.clear();
if(state.sampleLocEnable)
{
ret.multisample.sampleLocations.gridWidth = state.sampleLocations.gridSize.width;
ret.multisample.sampleLocations.gridHeight = state.sampleLocations.gridSize.height;
ret.multisample.sampleLocations.customLocations.reserve(state.sampleLocations.locations.size());
for(const VkSampleLocationEXT &loc : state.sampleLocations.locations)
{
ret.multisample.sampleLocations.customLocations.push_back({loc.x, loc.y, 0.0f, 0.0f});
}
}
// Color Blend
ret.colorBlend.alphaToCoverageEnable = state.alphaToCoverageEnable != VK_FALSE;
ret.colorBlend.alphaToOneEnable = state.alphaToOneEnable != VK_FALSE;
// find size if no static pipeline state
size_t numAttach = RDCMAX(state.colorBlendEnable.size(), state.colorBlendEquation.size());
numAttach = RDCMAX(numAttach, state.colorWriteEnable.size());
numAttach = RDCMAX(numAttach, state.colorWriteMask.size());
ret.colorBlend.blends.resize(numAttach);
for(size_t i = 0; i < numAttach; i++)
{
ret.colorBlend.blends[i].enabled =
(i < state.colorBlendEnable.size()) ? state.colorBlendEnable[i] != VK_FALSE : VK_FALSE;
// due to shared structs, this is slightly duplicated - Vulkan doesn't have separate states
// for logic operations
ret.colorBlend.blends[i].logicOperationEnabled = state.logicOpEnable != VK_FALSE;
ret.colorBlend.blends[i].logicOperation = MakeLogicOp(state.logicOp);
if(ret.colorBlend.blends[i].enabled && i < state.colorBlendEquation.size())
{
ret.colorBlend.blends[i].colorBlend.source =
MakeBlendMultiplier(state.colorBlendEquation[i].srcColorBlendFactor);
ret.colorBlend.blends[i].colorBlend.destination =
MakeBlendMultiplier(state.colorBlendEquation[i].dstColorBlendFactor);
ret.colorBlend.blends[i].colorBlend.operation =
MakeBlendOp(state.colorBlendEquation[i].colorBlendOp);
ret.colorBlend.blends[i].alphaBlend.source =
MakeBlendMultiplier(state.colorBlendEquation[i].srcAlphaBlendFactor);
ret.colorBlend.blends[i].alphaBlend.destination =
MakeBlendMultiplier(state.colorBlendEquation[i].dstAlphaBlendFactor);
ret.colorBlend.blends[i].alphaBlend.operation =
MakeBlendOp(state.colorBlendEquation[i].alphaBlendOp);
}
else
{
ret.colorBlend.blends[i].colorBlend.source = MakeBlendMultiplier(VK_BLEND_FACTOR_ZERO);
ret.colorBlend.blends[i].colorBlend.destination = MakeBlendMultiplier(VK_BLEND_FACTOR_ZERO);
ret.colorBlend.blends[i].colorBlend.operation = MakeBlendOp(VK_BLEND_OP_ADD);
ret.colorBlend.blends[i].alphaBlend.source = MakeBlendMultiplier(VK_BLEND_FACTOR_ZERO);
ret.colorBlend.blends[i].alphaBlend.destination = MakeBlendMultiplier(VK_BLEND_FACTOR_ZERO);
ret.colorBlend.blends[i].alphaBlend.operation = MakeBlendOp(VK_BLEND_OP_ADD);
}
ret.colorBlend.blends[i].writeMask =
(i < state.colorWriteMask.size()) ? (uint8_t)state.colorWriteMask[i] : 0;
if(i < state.colorWriteEnable.size() && !state.colorWriteEnable[i])
ret.colorBlend.blends[i].writeMask = 0;
}
ret.colorBlend.blendFactor = state.blendConst;
// Depth Stencil
ret.depthStencil.depthTestEnable = state.depthTestEnable != VK_FALSE;
ret.depthStencil.depthWriteEnable = state.depthWriteEnable != VK_FALSE;
ret.depthStencil.depthBoundsEnable = state.depthBoundsTestEnable != VK_FALSE;
ret.depthStencil.depthFunction = MakeCompareFunc(state.depthCompareOp);
ret.depthStencil.stencilTestEnable = state.stencilTestEnable != VK_FALSE;
ret.depthStencil.frontFace.passOperation = MakeStencilOp(state.front.passOp);
ret.depthStencil.frontFace.failOperation = MakeStencilOp(state.front.failOp);
ret.depthStencil.frontFace.depthFailOperation = MakeStencilOp(state.front.depthFailOp);
ret.depthStencil.frontFace.function = MakeCompareFunc(state.front.compareOp);
ret.depthStencil.backFace.passOperation = MakeStencilOp(state.back.passOp);
ret.depthStencil.backFace.failOperation = MakeStencilOp(state.back.failOp);
ret.depthStencil.backFace.depthFailOperation = MakeStencilOp(state.back.depthFailOp);
ret.depthStencil.backFace.function = MakeCompareFunc(state.back.compareOp);
ret.depthStencil.minDepthBounds = state.mindepth;
ret.depthStencil.maxDepthBounds = state.maxdepth;
ret.depthStencil.frontFace.reference = state.front.ref;
ret.depthStencil.frontFace.compareMask = state.front.compare;
ret.depthStencil.frontFace.writeMask = state.front.write;
ret.depthStencil.backFace.reference = state.back.ref;
ret.depthStencil.backFace.compareMask = state.back.compare;
ret.depthStencil.backFace.writeMask = state.back.write;
}
else
{
ret.graphics.pipelinePreRastLayoutResourceId = ResourceId();
ret.graphics.pipelineFragmentLayoutResourceId = ResourceId();
ret.graphics.flags = 0;
ret.vertexInput.attributes.clear();
ret.vertexInput.bindings.clear();
ret.vertexInput.vertexBuffers.clear();
VKPipe::Shader *stages[] = {
&ret.vertexShader, &ret.tessControlShader, &ret.tessEvalShader, &ret.geometryShader,
&ret.fragmentShader, &ret.taskShader, &ret.meshShader,
};
for(size_t i = 0; i < ARRAY_COUNT(stages); i++)
*stages[i] = VKPipe::Shader();
ret.viewportScissor.viewportScissors.clear();
ret.viewportScissor.discardRectangles.clear();
ret.viewportScissor.discardRectanglesExclusive = true;
ret.viewportScissor.depthNegativeOneToOne = false;
ret.colorBlend.blends.clear();
}
if(state.dynamicRendering.active)
{
VKPipe::RenderPass &rpState = ret.currentPass.renderpass;
VKPipe::Framebuffer &fbState = ret.currentPass.framebuffer;
const VulkanRenderState::DynamicRendering &dyn = state.dynamicRendering;
rpState.dynamic = true;
rpState.suspended = dyn.suspended;
rpState.feedbackLoop = false;
rpState.resourceId = ResourceId();
rpState.subpass = 0;
rpState.fragmentDensityOffsets.clear();
rpState.tileOnlyMSAASampleCount = 0;
fbState.resourceId = ResourceId();
// dynamic rendering does not provide a framebuffer dimension, it's implicit from the image
// views
fbState.width = 0;
fbState.height = 0;
fbState.layers = dyn.layerCount;
fbState.attachments.clear();
rpState.inputAttachments.clear();
rpState.colorAttachments.clear();
rpState.resolveAttachments.clear();
size_t attIdx = 0;
for(size_t i = 0; i < dyn.color.size(); i++)
{
fbState.attachments.push_back({});
ResourceId viewid = GetResID(dyn.color[i].imageView);
if(viewid != ResourceId())
{
fbState.attachments.back().view = rm->GetOriginalID(viewid);
ret.currentPass.framebuffer.attachments[attIdx].resource =
rm->GetOriginalID(c.m_ImageView[viewid].image);
fbState.attachments.back().format = MakeResourceFormat(c.m_ImageView[viewid].format);
fbState.attachments.back().firstMip = c.m_ImageView[viewid].range.baseMipLevel & 0xff;
fbState.attachments.back().firstSlice = c.m_ImageView[viewid].range.baseArrayLayer & 0xffff;
fbState.attachments.back().numMips = c.m_ImageView[viewid].range.levelCount & 0xff;
fbState.attachments.back().numSlices = c.m_ImageView[viewid].range.layerCount & 0xffff;
Convert(fbState.attachments.back().swizzle, c.m_ImageView[viewid].componentMapping);
}
else
{
fbState.attachments.back().view = ResourceId();
fbState.attachments.back().resource = ResourceId();
fbState.attachments.back().firstMip = 0;
fbState.attachments.back().firstSlice = 0;
fbState.attachments.back().numMips = 1;
fbState.attachments.back().numSlices = 1;
}
rpState.colorAttachments.push_back(uint32_t(attIdx++));
if(dyn.color[i].resolveMode && dyn.color[i].resolveImageView != VK_NULL_HANDLE)
{
fbState.attachments.push_back({});
viewid = GetResID(dyn.color[i].resolveImageView);
fbState.attachments.back().view = rm->GetOriginalID(viewid);
ret.currentPass.framebuffer.attachments[attIdx].resource =
rm->GetOriginalID(c.m_ImageView[viewid].image);
fbState.attachments.back().format = MakeResourceFormat(c.m_ImageView[viewid].format);
fbState.attachments.back().firstMip = c.m_ImageView[viewid].range.baseMipLevel & 0xff;
fbState.attachments.back().firstSlice = c.m_ImageView[viewid].range.baseArrayLayer & 0xffff;
fbState.attachments.back().numMips = c.m_ImageView[viewid].range.levelCount & 0xff;
fbState.attachments.back().numSlices = c.m_ImageView[viewid].range.layerCount & 0xffff;
Convert(fbState.attachments.back().swizzle, c.m_ImageView[viewid].componentMapping);
rpState.resolveAttachments.push_back(uint32_t(attIdx++));
}
}
if(dyn.depth.imageView != VK_NULL_HANDLE || dyn.stencil.imageView != VK_NULL_HANDLE)
{
fbState.attachments.push_back({});
ResourceId viewid = GetResID(dyn.depth.imageView);
if(dyn.depth.imageView == VK_NULL_HANDLE)
viewid = GetResID(dyn.stencil.imageView);
fbState.attachments.back().view = rm->GetOriginalID(viewid);
ret.currentPass.framebuffer.attachments[attIdx].resource =
rm->GetOriginalID(c.m_ImageView[viewid].image);
fbState.attachments.back().format = MakeResourceFormat(c.m_ImageView[viewid].format);
fbState.attachments.back().firstMip = c.m_ImageView[viewid].range.baseMipLevel & 0xff;
fbState.attachments.back().firstSlice = c.m_ImageView[viewid].range.baseArrayLayer & 0xffff;
fbState.attachments.back().numMips = c.m_ImageView[viewid].range.levelCount & 0xff;
fbState.attachments.back().numSlices = c.m_ImageView[viewid].range.layerCount & 0xffff;
Convert(fbState.attachments.back().swizzle, c.m_ImageView[viewid].componentMapping);
rpState.depthstencilAttachment = int32_t(attIdx++);
}
else
{
rpState.depthstencilAttachment = -1;
}
if(dyn.fragmentDensityView != VK_NULL_HANDLE)
{
fbState.attachments.push_back({});
ResourceId viewid = GetResID(dyn.fragmentDensityView);
fbState.attachments.back().view = rm->GetOriginalID(viewid);
ret.currentPass.framebuffer.attachments[attIdx].resource =
rm->GetOriginalID(c.m_ImageView[viewid].image);
fbState.attachments.back().format = MakeResourceFormat(c.m_ImageView[viewid].format);
fbState.attachments.back().firstMip = c.m_ImageView[viewid].range.baseMipLevel & 0xff;
fbState.attachments.back().firstSlice = c.m_ImageView[viewid].range.baseArrayLayer & 0xffff;
fbState.attachments.back().numMips = c.m_ImageView[viewid].range.levelCount & 0xff;
fbState.attachments.back().numSlices = c.m_ImageView[viewid].range.layerCount & 0xffff;
Convert(fbState.attachments.back().swizzle, c.m_ImageView[viewid].componentMapping);
rpState.fragmentDensityAttachment = int32_t(attIdx++);
}
else
{
rpState.fragmentDensityAttachment = -1;
}
if(dyn.shadingRateView != VK_NULL_HANDLE)
{
fbState.attachments.push_back({});
ResourceId viewid = GetResID(dyn.shadingRateView);
fbState.attachments.back().view = rm->GetOriginalID(viewid);
ret.currentPass.framebuffer.attachments[attIdx].resource =
rm->GetOriginalID(c.m_ImageView[viewid].image);
fbState.attachments.back().format = MakeResourceFormat(c.m_ImageView[viewid].format);
fbState.attachments.back().firstMip = c.m_ImageView[viewid].range.baseMipLevel & 0xff;
fbState.attachments.back().firstSlice = c.m_ImageView[viewid].range.baseArrayLayer & 0xffff;
fbState.attachments.back().numMips = c.m_ImageView[viewid].range.levelCount & 0xff;
fbState.attachments.back().numSlices = c.m_ImageView[viewid].range.layerCount & 0xffff;
Convert(fbState.attachments.back().swizzle, c.m_ImageView[viewid].componentMapping);
rpState.shadingRateAttachment = int32_t(attIdx++);
rpState.shadingRateTexelSize = {dyn.shadingRateTexelSize.width,
dyn.shadingRateTexelSize.height};
}
else
{
rpState.shadingRateAttachment = -1;
rpState.shadingRateTexelSize = {1, 1};
}
rpState.multiviews.clear();
for(uint32_t v = 0; v < 32; v++)
{
if(dyn.viewMask & (1 << v))
rpState.multiviews.push_back(v);
}
}
else if(state.GetRenderPass() != ResourceId())
{
// Renderpass
ret.currentPass.renderpass.dynamic = false;
ret.currentPass.renderpass.resourceId = rm->GetOriginalID(state.GetRenderPass());
ret.currentPass.renderpass.subpass = state.subpass;
ret.currentPass.renderpass.inputAttachments =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].inputAttachments;
ret.currentPass.renderpass.colorAttachments =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].colorAttachments;
ret.currentPass.renderpass.resolveAttachments =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].resolveAttachments;
ret.currentPass.renderpass.depthstencilAttachment =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].depthstencilAttachment;
ret.currentPass.renderpass.depthstencilResolveAttachment =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].depthstencilResolveAttachment;
ret.currentPass.renderpass.fragmentDensityAttachment =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].fragmentDensityAttachment;
ret.currentPass.renderpass.shadingRateAttachment =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].shadingRateAttachment;
VkExtent2D texelSize =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].shadingRateTexelSize;
ret.currentPass.renderpass.shadingRateTexelSize = {texelSize.width, texelSize.height};
ret.currentPass.renderpass.multiviews =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].multiviews;
ret.currentPass.renderpass.feedbackLoop =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].feedbackLoop;
ret.currentPass.renderpass.tileOnlyMSAASampleCount =
c.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].tileOnlyMSAASampleCount;
ResourceId fb = state.GetFramebuffer();
ret.currentPass.framebuffer.resourceId = rm->GetOriginalID(fb);
if(fb != ResourceId())
{
ret.currentPass.framebuffer.width = c.m_Framebuffer[fb].width;
ret.currentPass.framebuffer.height = c.m_Framebuffer[fb].height;
ret.currentPass.framebuffer.layers = c.m_Framebuffer[fb].layers;
ret.currentPass.framebuffer.attachments.resize(c.m_Framebuffer[fb].attachments.size());
for(size_t i = 0; i < c.m_Framebuffer[fb].attachments.size(); i++)
{
ResourceId viewid = state.GetFramebufferAttachments()[i];
if(viewid != ResourceId())
{
ret.currentPass.framebuffer.attachments[i].view = rm->GetOriginalID(viewid);
ret.currentPass.framebuffer.attachments[i].resource =
rm->GetOriginalID(c.m_ImageView[viewid].image);
ret.currentPass.framebuffer.attachments[i].format =
MakeResourceFormat(c.m_ImageView[viewid].format);
ret.currentPass.framebuffer.attachments[i].firstMip =
c.m_ImageView[viewid].range.baseMipLevel & 0xff;
ret.currentPass.framebuffer.attachments[i].firstSlice =
c.m_ImageView[viewid].range.baseArrayLayer & 0xffff;
ret.currentPass.framebuffer.attachments[i].numMips =
c.m_ImageView[viewid].range.levelCount & 0xff;
ret.currentPass.framebuffer.attachments[i].numSlices =
c.m_ImageView[viewid].range.layerCount & 0xffff;
Convert(ret.currentPass.framebuffer.attachments[i].swizzle,
c.m_ImageView[viewid].componentMapping);
}
else
{
ret.currentPass.framebuffer.attachments[i].view = ResourceId();
ret.currentPass.framebuffer.attachments[i].resource = ResourceId();
ret.currentPass.framebuffer.attachments[i].firstMip = 0;
ret.currentPass.framebuffer.attachments[i].firstSlice = 0;
ret.currentPass.framebuffer.attachments[i].numMips = 1;
ret.currentPass.framebuffer.attachments[i].numSlices = 1;
}
}
}
else
{
ret.currentPass.framebuffer.width = 0;
ret.currentPass.framebuffer.height = 0;
ret.currentPass.framebuffer.layers = 0;
}
ret.currentPass.renderpass.fragmentDensityOffsets.resize(state.fragmentDensityMapOffsets.size());
for(size_t i = 0; i < state.fragmentDensityMapOffsets.size(); i++)
{
const VkOffset2D &o = state.fragmentDensityMapOffsets[i];
ret.currentPass.renderpass.fragmentDensityOffsets[i] = Offset(o.x, o.y);
}
}
else
{
ret.currentPass.renderpass.resourceId = ResourceId();
ret.currentPass.renderpass.subpass = 0;
ret.currentPass.renderpass.inputAttachments.clear();
ret.currentPass.renderpass.colorAttachments.clear();
ret.currentPass.renderpass.resolveAttachments.clear();
ret.currentPass.renderpass.fragmentDensityOffsets.clear();
ret.currentPass.renderpass.depthstencilAttachment = -1;
ret.currentPass.renderpass.depthstencilResolveAttachment = -1;
ret.currentPass.renderpass.fragmentDensityAttachment = -1;
ret.currentPass.renderpass.shadingRateAttachment = -1;
ret.currentPass.renderpass.shadingRateTexelSize = {1, 1};
ret.currentPass.renderpass.tileOnlyMSAASampleCount = 0;
ret.currentPass.framebuffer.resourceId = ResourceId();
ret.currentPass.framebuffer.attachments.clear();
}
if(state.GetRenderPass() != ResourceId() || (state.dynamicRendering.active))
{
ret.currentPass.renderArea.x = state.renderArea.offset.x;
ret.currentPass.renderArea.y = state.renderArea.offset.y;
ret.currentPass.renderArea.width = state.renderArea.extent.width;
ret.currentPass.renderArea.height = state.renderArea.extent.height;
}
ret.currentPass.colorFeedbackAllowed = (state.feedbackAspects & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
ret.currentPass.depthFeedbackAllowed = (state.feedbackAspects & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
ret.currentPass.stencilFeedbackAllowed = (state.feedbackAspects & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
// Descriptor sets
ret.graphics.descriptorSets.resize(state.graphics.descSets.size());
ret.compute.descriptorSets.resize(state.compute.descSets.size());
// store dynamic offsets
{
rdcarray<VKPipe::DescriptorSet> *dsts[] = {
&ret.graphics.descriptorSets,
&ret.compute.descriptorSets,
};
const rdcarray<VulkanStatePipeline::DescriptorAndOffsets> *srcs[] = {
&state.graphics.descSets,
&state.compute.descSets,
};
for(size_t p = 0; p < ARRAY_COUNT(srcs); p++)
{
for(size_t i = 0; i < srcs[p]->size(); i++)
{
const VulkanStatePipeline::DescriptorAndOffsets &srcData = srcs[p]->at(i);
ResourceId sourceSet = srcData.descSet;
const uint32_t *srcOffset = srcData.offsets.begin();
VKPipe::DescriptorSet &destSet = dsts[p]->at(i);
destSet.dynamicOffsets.clear();
if(sourceSet == ResourceId())
continue;
destSet.dynamicOffsets.reserve(srcData.offsets.size());
VKPipe::DynamicOffset dynOffset;
const WrappedVulkan::DescriptorSetInfo &descSetState =
m_pDriver->m_DescriptorSetState[sourceSet];
const DescriptorSetSlot *first =
descSetState.data.binds.empty() ? NULL : descSetState.data.binds[0];
for(size_t b = 0; b < descSetState.data.binds.size(); b++)
{
const DescSetLayout::Binding &layoutBind =
c.m_DescSetLayout[descSetState.layout].bindings[b];
if(layoutBind.layoutDescType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC &&
layoutBind.layoutDescType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
continue;
uint64_t descriptorByteOffset = descSetState.data.binds[b] - first;
// inline UBOs aren't dynamic and variable size can't be used with dynamic buffers, so the
// count is what it is at definition time
for(uint32_t a = 0; a < layoutBind.descriptorCount; a++)
{
dynOffset.descriptorByteOffset = descriptorByteOffset + a;
dynOffset.dynamicBufferByteOffset = *srcOffset;
srcOffset++;
destSet.dynamicOffsets.push_back(dynOffset);
}
}
}
}
}
{
rdcarray<VKPipe::DescriptorSet> *dsts[] = {
&ret.graphics.descriptorSets,
&ret.compute.descriptorSets,
};
const rdcarray<VulkanStatePipeline::DescriptorAndOffsets> *srcs[] = {
&state.graphics.descSets,
&state.compute.descSets,
};
const VKDynamicShaderFeedback &usage = m_BindlessFeedback.Usage[eventId];
ret.shaderMessages = usage.messages;
for(size_t p = 0; p < ARRAY_COUNT(srcs); p++)
{
for(size_t i = 0; i < srcs[p]->size(); i++)
{
ResourceId sourceSet = (*srcs[p])[i].descSet;
VKPipe::DescriptorSet &destSet = (*dsts[p])[i];
if(sourceSet == ResourceId())
{
destSet.descriptorSetResourceId = ResourceId();
destSet.pushDescriptor = false;
destSet.layoutResourceId = ResourceId();
continue;
}
ResourceId layoutId = m_pDriver->m_DescriptorSetState[sourceSet].layout;
destSet.descriptorSetResourceId = rm->GetOriginalID(sourceSet);
destSet.pushDescriptor = (c.m_DescSetLayout[layoutId].flags &
VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR);
destSet.layoutResourceId = rm->GetOriginalID(layoutId);
}
}
}
// image layouts
{
size_t i = 0;
ret.images.resize(m_pDriver->m_ImageStates.size());
for(auto it = m_pDriver->m_ImageStates.begin(); it != m_pDriver->m_ImageStates.end(); ++it)
{
VKPipe::ImageData &img = ret.images[i];
if(rm->GetOriginalID(it->first) == it->first)
continue;
img.resourceId = rm->GetOriginalID(it->first);
LockedConstImageStateRef imState = it->second.LockRead();
img.layouts.resize(imState->subresourceStates.size());
auto subIt = imState->subresourceStates.begin();
for(size_t l = 0; l < img.layouts.size(); ++l, ++subIt)
{
img.layouts[l].name = ToStr(subIt->state().newLayout);
img.layouts[l].baseMip = subIt->range().baseMipLevel;
img.layouts[l].numMip = subIt->range().levelCount;
img.layouts[l].baseLayer = subIt->range().baseArrayLayer;
img.layouts[l].numLayer = subIt->range().layerCount;
}
if(img.layouts.empty())
{
img.layouts.push_back(VKPipe::ImageLayout());
img.layouts[0].name = "Unknown";
}
i++;
}
ret.images.resize(i);
}
if(state.conditionalRendering.buffer != ResourceId())
{
ret.conditionalRendering.bufferId = rm->GetOriginalID(state.conditionalRendering.buffer);
ret.conditionalRendering.byteOffset = state.conditionalRendering.offset;
ret.conditionalRendering.isInverted =
state.conditionalRendering.flags == VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
bytebuf data;
GetBufferData(state.conditionalRendering.buffer, state.conditionalRendering.offset,
sizeof(uint32_t), data);
uint32_t value;
memcpy(&value, data.data(), sizeof(uint32_t));
ret.conditionalRendering.isPassing = value != 0;
if(ret.conditionalRendering.isInverted)
ret.conditionalRendering.isPassing = !ret.conditionalRendering.isPassing;
}
}