Skip to content

keep driver's pipelineCacheUUID valid and use it for replay #3556

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 17, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 14 additions & 52 deletions renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp
Original file line number Diff line number Diff line change
@@ -234,10 +234,6 @@ void WrappedVulkan::vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevic
{
ObjDisp(physicalDevice)->GetPhysicalDeviceProperties(Unwrap(physicalDevice), pProperties);

MakeFakeUUID();

memcpy(pProperties->pipelineCacheUUID, fakeRenderDocUUID, VK_UUID_SIZE);

ClampPhysDevAPIVersion(pProperties, physicalDevice);
}

@@ -639,57 +635,25 @@ void WrappedVulkan::vkGetRenderAreaGranularity(VkDevice device, VkRenderPass ren
VkResult WrappedVulkan::vkGetPipelineCacheData(VkDevice device, VkPipelineCache pipelineCache,
size_t *pDataSize, void *pData)
{
// required header and 4 NULL bytes
size_t totalSize = sizeof(VkPipelineCacheHeaderVersionOne) + 4;

if(pDataSize && !pData)
*pDataSize = totalSize;

if(pDataSize && pData)
{
if(*pDataSize < totalSize)
{
memset(pData, 0, *pDataSize);
return VK_INCOMPLETE;
}

VkPipelineCacheHeaderVersionOne *header = (VkPipelineCacheHeaderVersionOne *)pData;

RDCCOMPILE_ASSERT(sizeof(VkPipelineCacheHeaderVersionOne) == 16 + VK_UUID_SIZE,
"Pipeline cache header size is wrong");

header->headerSize = sizeof(VkPipelineCacheHeaderVersionOne);
header->headerVersion = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
// just in case the user expects a valid vendorID/deviceID, write the real one
// MULTIDEVICE need to get the right physical device for this device
header->vendorID = m_PhysicalDeviceData.props.vendorID;
header->deviceID = m_PhysicalDeviceData.props.deviceID;

MakeFakeUUID();

memcpy(header->pipelineCacheUUID, fakeRenderDocUUID, VK_UUID_SIZE);

RDCCOMPILE_ASSERT(VK_UUID_SIZE == 16, "VK_UUID_SIZE has changed");

// empty bytes
uint32_t *ptr = (uint32_t *)(header + 1);
*ptr = 0;
}

// we don't want the application to use pipeline caches at all, and especially
// don't want to return any data for future use. We thus return a technically
// valid but empty pipeline cache. Our UUID changes every run so in theory the
// application should never provide an old cache, but just in case we will nop
// it out in create pipeline cache
return VK_SUCCESS;
return ObjDisp(device)->GetPipelineCacheData(Unwrap(device), Unwrap(pipelineCache), pDataSize,
pData);
}

VkResult WrappedVulkan::vkMergePipelineCaches(VkDevice device, VkPipelineCache destCache,
uint32_t srcCacheCount,
const VkPipelineCache *pSrcCaches)
{
// do nothing, our pipeline caches are always dummies
return VK_SUCCESS;
rdcarray<VkPipelineCache> unwrappedPipelineCaches;

unwrappedPipelineCaches.reserve(srcCacheCount);
for(uint32_t cacheIndex = 0; cacheIndex < srcCacheCount; cacheIndex++)
{
unwrappedPipelineCaches.push_back(Unwrap(pSrcCaches[cacheIndex]));
}

return ObjDisp(device)->MergePipelineCaches(Unwrap(device), Unwrap(destCache), srcCacheCount,
unwrappedPipelineCaches.data());
}

VkResult WrappedVulkan::vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
@@ -893,10 +857,6 @@ void WrappedVulkan::vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevi
{
ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), pProperties);

MakeFakeUUID();

memcpy(pProperties->properties.pipelineCacheUUID, fakeRenderDocUUID, VK_UUID_SIZE);

ClampPhysDevAPIVersion(&pProperties->properties, physicalDevice);

// internal RenderDoc UUID for shader object binary
@@ -906,6 +866,8 @@ void WrappedVulkan::vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevi

if(shadObj)
{
MakeFakeUUID();

memcpy(shadObj->shaderBinaryUUID, fakeRenderDocUUID, VK_UUID_SIZE);
}
}
92 changes: 41 additions & 51 deletions renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp
Original file line number Diff line number Diff line change
@@ -29,16 +29,22 @@

RDOC_EXTERN_CONFIG(bool, Replay_Debug_SingleThreadedCompilation);

static RDResult DeferredPipelineCompile(VkDevice device,
RDOC_CONFIG(bool, Vulkan_Debug_UsePipelineCacheForReplay, true,
"Use application-provided pipeline cache when compiling shaders on replay");

static RDResult DeferredPipelineCompile(VkDevice device, VkPipelineCache pipelineCache,
const VkGraphicsPipelineCreateInfo &createInfo,
WrappedVkPipeline *wrappedPipe)
{
if(!Vulkan_Debug_UsePipelineCacheForReplay())
pipelineCache = VK_NULL_HANDLE;

byte *mem = AllocAlignedBuffer(GetNextPatchSize(&createInfo));
VkGraphicsPipelineCreateInfo *unwrapped =
UnwrapStructAndChain(CaptureState::LoadingReplaying, mem, &createInfo);

VkPipeline realPipe;
VkResult ret = ObjDisp(device)->CreateGraphicsPipelines(Unwrap(device), VK_NULL_HANDLE, 1,
VkResult ret = ObjDisp(device)->CreateGraphicsPipelines(Unwrap(device), Unwrap(pipelineCache), 1,
unwrapped, NULL, &realPipe);

FreeAlignedBuffer((byte *)unwrapped);
@@ -54,16 +60,19 @@ static RDResult DeferredPipelineCompile(VkDevice device,
return ResultCode::Succeeded;
}

static RDResult DeferredPipelineCompile(VkDevice device,
static RDResult DeferredPipelineCompile(VkDevice device, VkPipelineCache pipelineCache,
const VkComputePipelineCreateInfo &createInfo,
WrappedVkPipeline *wrappedPipe)
{
if(!Vulkan_Debug_UsePipelineCacheForReplay())
pipelineCache = VK_NULL_HANDLE;

byte *mem = AllocAlignedBuffer(GetNextPatchSize(&createInfo));
VkComputePipelineCreateInfo *unwrapped =
UnwrapStructAndChain(CaptureState::LoadingReplaying, mem, &createInfo);

VkPipeline realPipe;
VkResult ret = ObjDisp(device)->CreateComputePipelines(Unwrap(device), VK_NULL_HANDLE, 1,
VkResult ret = ObjDisp(device)->CreateComputePipelines(Unwrap(device), Unwrap(pipelineCache), 1,
unwrapped, NULL, &realPipe);

FreeAlignedBuffer((byte *)unwrapped);
@@ -79,12 +88,15 @@ static RDResult DeferredPipelineCompile(VkDevice device,
return ResultCode::Succeeded;
}

static RDResult DeferredPipelineCompile(VkDevice device,
static RDResult DeferredPipelineCompile(VkDevice device, VkPipelineCache pipelineCache,
const VkRayTracingPipelineCreateInfoKHR &createInfo,
const bytebuf &replayHandles,
uint32_t captureReplayHandleSize,
WrappedVkPipeline *wrappedPipe)
{
if(!Vulkan_Debug_UsePipelineCacheForReplay())
pipelineCache = VK_NULL_HANDLE;

byte *mem = AllocAlignedBuffer(GetNextPatchSize(&createInfo));
VkRayTracingPipelineCreateInfoKHR *unwrapped =
UnwrapStructAndChain(CaptureState::LoadingReplaying, mem, &createInfo);
@@ -98,7 +110,7 @@ static RDResult DeferredPipelineCompile(VkDevice device,

VkPipeline realPipe;
VkResult ret = ObjDisp(device)->CreateRayTracingPipelinesKHR(
Unwrap(device), VK_NULL_HANDLE, VK_NULL_HANDLE, 1, unwrapped, NULL, &realPipe);
Unwrap(device), VK_NULL_HANDLE, Unwrap(pipelineCache), 1, unwrapped, NULL, &realPipe);

FreeAlignedBuffer((byte *)unwrapped);

@@ -660,18 +672,7 @@ VkResult WrappedVulkan::vkCreatePipelineCache(VkDevice device,
const VkAllocationCallbacks *,
VkPipelineCache *pPipelineCache)
{
// pretend the user didn't provide any cache data

VkPipelineCacheCreateInfo createInfo = *pCreateInfo;
createInfo.initialDataSize = 0;
createInfo.pInitialData = NULL;

if(pCreateInfo->initialDataSize > 0)
{
RDCWARN(
"Application provided pipeline cache data! This is invalid, as RenderDoc reports "
"incompatibility with previous caches");
}

VkResult ret;
SERIALISE_TIME_CALL(ret = ObjDisp(device)->CreatePipelineCache(Unwrap(device), &createInfo, NULL,
@@ -726,10 +727,6 @@ bool WrappedVulkan::Serialise_vkCreateGraphicsPipelines(
VkPipeline pipe = VK_NULL_HANDLE;

VkRenderPass origRP = CreateInfo.renderPass;
VkPipelineCache origCache = pipelineCache;

// don't use pipeline caches on replay
pipelineCache = VK_NULL_HANDLE;

// if we have pipeline executable properties, capture the data
if(GetExtensions(NULL).ext_KHR_pipeline_executable_properties)
@@ -852,8 +849,8 @@ bool WrappedVulkan::Serialise_vkCreateGraphicsPipelines(
}

DerivedResource(device, Pipeline);
if(origCache != VK_NULL_HANDLE)
DerivedResource(origCache, Pipeline);
if(pipelineCache != VK_NULL_HANDLE)
DerivedResource(pipelineCache, Pipeline);
if(OrigCreateInfo.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)
{
if(OrigCreateInfo.basePipelineHandle != VK_NULL_HANDLE)
@@ -887,8 +884,8 @@ bool WrappedVulkan::Serialise_vkCreateGraphicsPipelines(
{
for(rdcpair<VkGraphicsPipelineCreateInfo, VkPipeline> &deferredPipe : pipelinesToCompile)
{
RDResult res =
DeferredPipelineCompile(device, deferredPipe.first, GetWrapped(deferredPipe.second));
RDResult res = DeferredPipelineCompile(device, pipelineCache, deferredPipe.first,
GetWrapped(deferredPipe.second));

if(res != ResultCode::Succeeded)
{
@@ -908,10 +905,11 @@ bool WrappedVulkan::Serialise_vkCreateGraphicsPipelines(
{
WrappedVkPipeline *wrappedPipe = GetWrapped(deferredPipe.second);
wrappedPipe->deferredJob = Threading::JobSystem::AddJob(
[wrappedVulkan = this, device, createInfo = deferredPipe.first, wrappedPipe]() {
[wrappedVulkan = this, device, pipelineCache, createInfo = deferredPipe.first,
wrappedPipe]() {
PerformanceTimer timer;
wrappedVulkan->CheckDeferredResult(
DeferredPipelineCompile(device, createInfo, wrappedPipe));
DeferredPipelineCompile(device, pipelineCache, createInfo, wrappedPipe));
wrappedVulkan->AddDeferredTime(timer.GetMilliseconds());
},
parents);
@@ -1074,11 +1072,6 @@ bool WrappedVulkan::Serialise_vkCreateComputePipelines(SerialiserType &ser, VkDe
{
VkPipeline pipe = VK_NULL_HANDLE;

VkPipelineCache origCache = pipelineCache;

// don't use pipeline caches on replay
pipelineCache = VK_NULL_HANDLE;

// if we have pipeline executable properties, capture the data
if(GetExtensions(NULL).ext_KHR_pipeline_executable_properties)
{
@@ -1152,7 +1145,7 @@ bool WrappedVulkan::Serialise_vkCreateComputePipelines(SerialiserType &ser, VkDe

if(Replay_Debug_SingleThreadedCompilation())
{
RDResult res = DeferredPipelineCompile(device, OrigCreateInfo, GetWrapped(pipe));
RDResult res = DeferredPipelineCompile(device, pipelineCache, OrigCreateInfo, GetWrapped(pipe));
Deserialise(OrigCreateInfo);

if(res != ResultCode::Succeeded)
@@ -1164,20 +1157,20 @@ bool WrappedVulkan::Serialise_vkCreateComputePipelines(SerialiserType &ser, VkDe
else
{
WrappedVkPipeline *wrappedPipe = GetWrapped(pipe);
wrappedPipe->deferredJob =
Threading::JobSystem::AddJob([wrappedVulkan = this, device, OrigCreateInfo, wrappedPipe]() {
wrappedPipe->deferredJob = Threading::JobSystem::AddJob(
[wrappedVulkan = this, device, pipelineCache, OrigCreateInfo, wrappedPipe]() {
PerformanceTimer timer;
wrappedVulkan->CheckDeferredResult(
DeferredPipelineCompile(device, OrigCreateInfo, wrappedPipe));
DeferredPipelineCompile(device, pipelineCache, OrigCreateInfo, wrappedPipe));
wrappedVulkan->AddDeferredTime(timer.GetMilliseconds());

Deserialise(OrigCreateInfo);
});
}

DerivedResource(device, Pipeline);
if(origCache != VK_NULL_HANDLE)
DerivedResource(origCache, Pipeline);
if(pipelineCache != VK_NULL_HANDLE)
DerivedResource(pipelineCache, Pipeline);
if(OrigCreateInfo.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)
{
if(OrigCreateInfo.basePipelineHandle != VK_NULL_HANDLE)
@@ -1360,11 +1353,6 @@ bool WrappedVulkan::Serialise_vkCreateRayTracingPipelinesKHR(

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
@@ -1390,8 +1378,8 @@ bool WrappedVulkan::Serialise_vkCreateRayTracingPipelinesKHR(
pipeInfo.Init(GetResourceManager(), m_CreationInfo, live, &OrigCreateInfo);

DerivedResource(device, Pipeline);
if(origCache != VK_NULL_HANDLE)
DerivedResource(origCache, Pipeline);
if(pipelineCache != VK_NULL_HANDLE)
DerivedResource(pipelineCache, Pipeline);
if(OrigCreateInfo.flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)
{
if(OrigCreateInfo.basePipelineHandle != VK_NULL_HANDLE)
@@ -1418,8 +1406,9 @@ bool WrappedVulkan::Serialise_vkCreateRayTracingPipelinesKHR(

if(Replay_Debug_SingleThreadedCompilation())
{
RDResult res = DeferredPipelineCompile(device, OrigCreateInfo, *OrigReplayHandles,
captureReplayHandleSize, GetWrapped(pipe));
RDResult res =
DeferredPipelineCompile(device, pipelineCache, OrigCreateInfo, *OrigReplayHandles,
captureReplayHandleSize, GetWrapped(pipe));
if(res == ResultCode::APIHardwareUnsupported)
res.message = rdcstr(res.message) + "\n" + GetPhysDeviceCompatString(false, false);
Deserialise(OrigCreateInfo);
@@ -1435,11 +1424,12 @@ bool WrappedVulkan::Serialise_vkCreateRayTracingPipelinesKHR(
{
WrappedVkPipeline *wrappedPipe = GetWrapped(pipe);
wrappedPipe->deferredJob = Threading::JobSystem::AddJob(
[wrappedVulkan = this, device, OrigCreateInfo, OrigReplayHandles, captureReplayHandleSize,
wrappedPipe]() {
[wrappedVulkan = this, device, pipelineCache, OrigCreateInfo, OrigReplayHandles,
captureReplayHandleSize, wrappedPipe]() {
PerformanceTimer timer;
RDResult res = DeferredPipelineCompile(device, OrigCreateInfo, *OrigReplayHandles,
captureReplayHandleSize, wrappedPipe);
RDResult res =
DeferredPipelineCompile(device, pipelineCache, OrigCreateInfo, *OrigReplayHandles,
captureReplayHandleSize, wrappedPipe);
wrappedVulkan->AddDeferredTime(timer.GetMilliseconds());
if(res == ResultCode::APIHardwareUnsupported)
res.message = rdcstr(res.message) + "\n" +