Implement RT pipeline creation & capture/replay handle folding in

This commit is contained in:
baldurk
2024-06-26 17:07:09 +01:00
parent 9e521e29a6
commit 6fa26849cd
4 changed files with 327 additions and 0 deletions
+2
View File
@@ -483,6 +483,8 @@ private:
bool m_AccelerationStructures = false;
bool m_ShaderObject = false;
uint32_t m_RTCaptureReplayHandleSize = 0;
PFN_vkSetDeviceLoaderData m_SetDeviceLoaderData;
InstanceDeviceInfo m_EnabledExtensions;
+57
View File
@@ -1857,6 +1857,63 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk
setLayoutInfos);
}
void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan,
VulkanCreationInfo &info, ResourceId id,
const VkRayTracingPipelineCreateInfoKHR *pCreateInfo)
{
flags = pCreateInfo->flags;
graphicsPipe = false;
compLayout = GetResID(pCreateInfo->layout);
descSetLayouts = info.m_PipelineLayout[compLayout].descSetLayouts;
topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
primitiveRestartEnable = false;
patchControlPoints = 0;
tessellationDomainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT;
viewportCount = 0;
// VkPipelineRasterStateCreateInfo
depthClampEnable = false;
rasterizerDiscardEnable = false;
polygonMode = VK_POLYGON_MODE_FILL;
cullMode = VK_CULL_MODE_NONE;
frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
// VkPipelineRasterizationConservativeStateCreateInfoEXT
conservativeRasterizationMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
extraPrimitiveOverestimationSize = 0.0f;
// VkPipelineMultisampleStateCreateInfo
rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
sampleShadingEnable = false;
minSampleShading = 1.0f;
sampleMask = ~0U;
// VkPipelineDepthStencilStateCreateInfo
depthTestEnable = false;
depthWriteEnable = false;
depthCompareOp = VK_COMPARE_OP_ALWAYS;
depthBoundsEnable = false;
stencilTestEnable = false;
RDCEraseEl(front);
RDCEraseEl(back);
// VkPipelineColorBlendStateCreateInfo
alphaToCoverageEnable = false;
logicOpEnable = false;
logicOp = VK_LOGIC_OP_NO_OP;
rdcarray<const DescSetLayout *> setLayoutInfos;
for(ResourceId setLayout : descSetLayouts)
setLayoutInfos.push_back(&info.m_DescSetLayout[setLayout]);
}
void VulkanCreationInfo::PipelineLayout::Init(VulkanResourceManager *resourceMan,
VulkanCreationInfo &info,
const VkPipelineLayoutCreateInfo *pCreateInfo)
+2
View File
@@ -254,6 +254,8 @@ struct VulkanCreationInfo
const VkGraphicsPipelineCreateInfo *pCreateInfo);
void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, ResourceId id,
const VkComputePipelineCreateInfo *pCreateInfo);
void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, ResourceId id,
const VkRayTracingPipelineCreateInfoKHR *pCreateInfo);
bool graphicsPipe = false;
@@ -1159,6 +1159,168 @@ bool WrappedVulkan::Serialise_vkCreateRayTracingPipelinesKHR(
const VkRayTracingPipelineCreateInfoKHR *pCreateInfos, const VkAllocationCallbacks *pAllocator,
VkPipeline *pPipelines)
{
SERIALISE_ELEMENT(device);
SERIALISE_ELEMENT(pipelineCache);
SERIALISE_ELEMENT(createInfoCount);
SERIALISE_ELEMENT_LOCAL(CreateInfo, *pCreateInfos).Important();
SERIALISE_ELEMENT_OPT(pAllocator);
SERIALISE_ELEMENT_LOCAL(Pipeline, GetResID(*pPipelines)).TypedAs("VkPipeline"_lit);
uint32_t captureReplayHandleSize = 0;
bytebuf captureReplayHandles;
if(ser.IsWriting())
{
if(m_RTCaptureReplayHandleSize == 0)
{
VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayProps = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR,
};
VkPhysicalDeviceProperties2 propBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
propBase.pNext = &rayProps;
ObjDisp(m_PhysicalDevice)->GetPhysicalDeviceProperties2(Unwrap(m_PhysicalDevice), &propBase);
m_RTCaptureReplayHandleSize = rayProps.shaderGroupHandleCaptureReplaySize;
}
RDCASSERTNOTEQUAL(m_RTCaptureReplayHandleSize, 0);
captureReplayHandleSize = m_RTCaptureReplayHandleSize;
captureReplayHandles.resize(captureReplayHandleSize * pCreateInfos->groupCount);
ObjDisp(device)->GetRayTracingCaptureReplayShaderGroupHandlesKHR(
Unwrap(device), Unwrap(*pPipelines), 0, pCreateInfos->groupCount,
captureReplayHandles.size(), captureReplayHandles.data());
}
SERIALISE_ELEMENT(captureReplayHandleSize).Hidden();
SERIALISE_ELEMENT(captureReplayHandles).Hidden();
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
if(m_RTCaptureReplayHandleSize == 0)
{
VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayProps = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR,
};
VkPhysicalDeviceProperties2 propBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
propBase.pNext = &rayProps;
ObjDisp(m_PhysicalDevice)->GetPhysicalDeviceProperties2(Unwrap(m_PhysicalDevice), &propBase);
m_RTCaptureReplayHandleSize = rayProps.shaderGroupHandleCaptureReplaySize;
}
RDCASSERTNOTEQUAL(m_RTCaptureReplayHandleSize, 0);
if(m_RTCaptureReplayHandleSize != captureReplayHandleSize)
{
SET_ERROR_RESULT(
m_FailedReplayResult, ResultCode::APIHardwareUnsupported,
"Failed to re-create RT PSO as capture/replay handle size changed from %u to %u.\n"
"\n%s",
captureReplayHandleSize, m_RTCaptureReplayHandleSize,
GetPhysDeviceCompatString(false, false).c_str());
return false;
}
VkPipeline pipe = VK_NULL_HANDLE;
VkPipelineCache origCache = pipelineCache;
// don't use pipeline caches on replay
pipelineCache = VK_NULL_HANDLE;
// don't fail when a compile is required because we don't currently replay caches so this will
// always happen. This still allows application to use this flag at runtime where it will be
// valid
CreateInfo.flags &= ~VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT;
VkRayTracingPipelineCreateInfoKHR *unwrapped = UnwrapInfos(m_State, &CreateInfo, 1);
// patch in the capture/replay handles we saved
VkRayTracingShaderGroupCreateInfoKHR *groups =
(VkRayTracingShaderGroupCreateInfoKHR *)unwrapped->pGroups;
for(uint32_t i = 0; i < unwrapped->groupCount; i++)
groups[i].pShaderGroupCaptureReplayHandle =
captureReplayHandles.data() + captureReplayHandleSize * i;
VkResult ret = ObjDisp(device)->CreateRayTracingPipelinesKHR(
Unwrap(device), VK_NULL_HANDLE, Unwrap(pipelineCache), 1, unwrapped, NULL, &pipe);
AddResource(Pipeline, ResourceType::PipelineState, "RT Pipeline");
if(ret == VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS)
{
SET_ERROR_RESULT(
m_FailedReplayResult, ResultCode::APIHardwareUnsupported,
"Failed to re-create RT PSO because capture/replay handle was incompatible.\n"
"\n%s",
GetPhysDeviceCompatString(false, false).c_str());
return false;
}
else if(ret != VK_SUCCESS)
{
SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed,
"Failed creating RT pipeline, VkResult: %s", ToStr(ret).c_str());
return false;
}
else
{
ResourceId live;
if(GetResourceManager()->HasWrapper(ToTypedHandle(pipe)))
{
live = GetResourceManager()->GetNonDispWrapper(pipe)->id;
// destroy this instance of the duplicate, as we must have matching create/destroy
// calls and there won't be a wrapped resource hanging around to destroy this one.
ObjDisp(device)->DestroyPipeline(Unwrap(device), pipe, NULL);
// whenever the new ID is requested, return the old ID, via replacements.
GetResourceManager()->ReplaceResource(Pipeline, GetResourceManager()->GetOriginalID(live));
}
else
{
live = GetResourceManager()->WrapResource(Unwrap(device), pipe);
GetResourceManager()->AddLiveResource(Pipeline, pipe);
VulkanCreationInfo::Pipeline &pipeInfo = m_CreationInfo.m_Pipeline[live];
pipeInfo.Init(GetResourceManager(), m_CreationInfo, live, &CreateInfo);
}
}
DerivedResource(device, Pipeline);
if(origCache != VK_NULL_HANDLE)
DerivedResource(origCache, Pipeline);
if(CreateInfo.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)
{
if(CreateInfo.basePipelineHandle != VK_NULL_HANDLE)
DerivedResource(CreateInfo.basePipelineHandle, Pipeline);
}
if(CreateInfo.layout != VK_NULL_HANDLE)
DerivedResource(CreateInfo.layout, Pipeline);
for(uint32_t i = 0; i < CreateInfo.stageCount; i++)
{
if(CreateInfo.pStages[i].module != VK_NULL_HANDLE)
DerivedResource(CreateInfo.pStages[i].module, Pipeline);
}
if(CreateInfo.pLibraryInfo)
{
for(uint32_t l = 0; l < CreateInfo.pLibraryInfo->libraryCount; l++)
{
DerivedResource(CreateInfo.pLibraryInfo->pLibraries[l], Pipeline);
}
}
}
return true;
}
@@ -1168,11 +1330,115 @@ VkResult WrappedVulkan::vkCreateRayTracingPipelinesKHR(
const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines)
{
VkResult ret;
// to be extra sure just in case the driver doesn't, set pipelines to VK_NULL_HANDLE first.
for(uint32_t i = 0; i < createInfoCount; i++)
pPipelines[i] = VK_NULL_HANDLE;
// deferred operations are currently not wrapped
SERIALISE_TIME_CALL(ret = ObjDisp(device)->CreateRayTracingPipelinesKHR(
Unwrap(device), deferredOperation, Unwrap(pipelineCache), createInfoCount,
UnwrapInfos(m_State, pCreateInfos, createInfoCount), NULL, pPipelines));
if(ret == VK_SUCCESS || ret == VK_PIPELINE_COMPILE_REQUIRED)
{
for(uint32_t i = 0; i < createInfoCount; i++)
{
// any pipelines that are VK_NULL_HANDLE, silently ignore as they failed but we might have
// successfully created some before then.
if(pPipelines[i] == VK_NULL_HANDLE)
continue;
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), pPipelines[i]);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
VkRayTracingPipelineCreateInfoKHR modifiedCreateInfo;
const VkRayTracingPipelineCreateInfoKHR *createInfo = &pCreateInfos[i];
if(createInfo->flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)
{
// since we serialise one by one, we need to fixup basePipelineIndex
if(createInfo->basePipelineIndex != -1 && createInfo->basePipelineIndex < (int)i)
{
modifiedCreateInfo = *createInfo;
modifiedCreateInfo.basePipelineHandle =
pPipelines[modifiedCreateInfo.basePipelineIndex];
modifiedCreateInfo.basePipelineIndex = -1;
createInfo = &modifiedCreateInfo;
}
}
SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCreateRayTracingPipelinesKHR);
Serialise_vkCreateRayTracingPipelinesKHR(ser, device, deferredOperation, pipelineCache, 1,
createInfo, NULL, &pPipelines[i]);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(pPipelines[i]);
record->AddChunk(chunk);
if(pCreateInfos[i].flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)
{
if(pCreateInfos[i].basePipelineHandle != VK_NULL_HANDLE)
{
VkResourceRecord *baserecord = GetRecord(pCreateInfos[i].basePipelineHandle);
record->AddParent(baserecord);
RDCDEBUG("Creating pipeline %s base is %s", ToStr(record->GetResourceID()).c_str(),
ToStr(baserecord->GetResourceID()).c_str());
}
else if(pCreateInfos[i].basePipelineIndex != -1 &&
pCreateInfos[i].basePipelineIndex < (int)i)
{
VkResourceRecord *baserecord = GetRecord(pPipelines[pCreateInfos[i].basePipelineIndex]);
record->AddParent(baserecord);
}
}
if(pipelineCache != VK_NULL_HANDLE)
{
VkResourceRecord *cacherecord = GetRecord(pipelineCache);
record->AddParent(cacherecord);
}
if(pCreateInfos[i].layout != VK_NULL_HANDLE)
{
VkResourceRecord *layoutrecord = GetRecord(pCreateInfos[i].layout);
record->AddParent(layoutrecord);
}
for(uint32_t s = 0; s < pCreateInfos[i].stageCount; s++)
{
VkResourceRecord *modulerecord = GetRecord(pCreateInfos[i].pStages[s].module);
if(modulerecord)
record->AddParent(modulerecord);
}
if(pCreateInfos[i].pLibraryInfo)
{
for(uint32_t l = 0; l < pCreateInfos[i].pLibraryInfo->libraryCount; l++)
{
record->AddParent(GetRecord(pCreateInfos[i].pLibraryInfo->pLibraries[l]));
}
}
}
else
{
GetResourceManager()->AddLiveResource(id, pPipelines[i]);
m_CreationInfo.m_Pipeline[id].Init(GetResourceManager(), m_CreationInfo, id,
&pCreateInfos[i]);
}
}
}
return ret;
}