diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index c4fd994fa..ed85533c6 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -686,9 +686,11 @@ struct MemMapState { MemMapState() : mapOffset(0), mapSize(0) - , mapFlushed(false), mapCoherent(false), mappedPtr(NULL), refData(NULL) + , needRefData(false), mapFlushed(false), mapCoherent(false) + , mappedPtr(NULL), refData(NULL) { } VkDeviceSize mapOffset, mapSize; + bool needRefData; bool mapFlushed; bool mapCoherent; byte *mappedPtr; diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp index 06c3b7e29..4571b3498 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp @@ -593,16 +593,26 @@ VkResult WrappedVulkan::vkQueueSubmit( (VkDeviceMemory)(uint64_t)record->Resource, state.mapOffset+diffStart, diffEnd-diffStart }; + // this causes the call within to allocate state.refData and copy what was + // serialised into it. We want to copy *precisely* the serialised data, + // otherwise there is a gap in time between serialising out a snapshot of + // the buffer and whenever we then copy into the ref data, e.g. below. + // during this time, data could be written to the buffer and it won't have + // been caught in the serialised snapshot, and if it doesn't change then + // it *also* won't be caught in any future FindDiffRange() calls. + // + // Note: it's still possible that data is being written to by the + // application while it's being serialised out in the snapshot below. That + // is OK, since the application is responsible for ensuring it's not writing + // data that would be needed by the GPU in this submit. As long as the + // refdata we use for future use is identical to what was serialised, we + // shouldn't miss anything + state.needRefData = true; vkFlushMappedMemoryRanges(dev, 1, &range); state.mapFlushed = false; } GetResourceManager()->MarkPendingDirty(record->GetResourceID()); - - // allocate ref data so we can compare next time to minimise serialised data - if(state.refData == NULL) - state.refData = Serialiser::AllocAlignedBuffer((size_t)state.mapSize, 64); - memcpy(state.refData, state.mappedPtr, (size_t)state.mapSize); } else { diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index e22fbb7d8..a76a22464 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -487,6 +487,25 @@ bool WrappedVulkan::Serialise_vkFlushMappedMemoryRanges( SERIALISE_ELEMENT(uint64_t, memSize, pMemRanges->size); SERIALISE_ELEMENT_BUF(byte*, data, state->mappedPtr + (size_t)memOffset, (size_t)memSize); + // if we need to save off this serialised buffer as reference for future comparison, + // do so now. See the call to vkFlushMappedMemoryRanges in WrappedVulkan::vkQueueSubmit() + if(m_State >= WRITING && state->needRefData && !state->refData) + { + // if we're in this case, the range should be for the whole memory region. + RDCASSERT(memOffset == 0 && memSize == state->mapSize); + + // it's no longer safe to use state->mappedPtr, we need to save *precisely* what + // was serialised. We do this by copying out of the serialiser since we know this + // memory is not changing + size_t offs = size_t(localSerialiser->GetOffset() - memSize); + + byte *serialisedData = localSerialiser->GetRawPtr(offs); + + // allocate ref data so we can compare next time to minimise serialised data + state->refData = Serialiser::AllocAlignedBuffer((size_t)state->mapSize); + memcpy(state->refData, serialisedData, (size_t)state->mapSize); + } + if(m_State < WRITING) { device = GetResourceManager()->GetLiveHandle(devId);