From b6c7a406c1d6ef5acf8ee6b5bf63ba6b9a91c53b Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 21 Sep 2015 13:45:34 +0200 Subject: [PATCH] Add first-pass implementation of vkFlushMappedMemoryRanges * Assuming that if we hit a flush, then on unmap we don't need to serialise everything. This assumption might not hold in general --- renderdoc/driver/vulkan/vk_common.h | 1 + renderdoc/driver/vulkan/vk_core.cpp | 4 + renderdoc/driver/vulkan/vk_resources.h | 3 +- .../vulkan/wrappers/vk_resource_funcs.cpp | 97 ++++++++++++++++--- 4 files changed, 93 insertions(+), 12 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index ea3e4e46f..3f7326a81 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -148,6 +148,7 @@ enum VulkanChunkType ALLOC_MEM, UNMAP_MEM, + FLUSH_MEM, FREE_MEM, CREATE_CMD_POOL, diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 219c70bb4..e7a277b52 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -50,6 +50,7 @@ const char *VkChunkNames[] = "vkAllocMemory", "vkUnmapMemory", + "vkFlushMappedMemoryRanges", "vkFreeMemory", "vkCreateCommandPool", @@ -884,6 +885,9 @@ void WrappedVulkan::ProcessChunk(uint64_t offset, VulkanChunkType context) case UNMAP_MEM: Serialise_vkUnmapMemory(VK_NULL_HANDLE, VK_NULL_HANDLE); break; + case FLUSH_MEM: + Serialise_vkFlushMappedMemoryRanges(VK_NULL_HANDLE, 0, NULL); + break; case FREE_MEM: // VKTODOMED see vkFreeMemory //Serialise_vkFreeMemory(VK_NULL_HANDLE, VK_NULL_HANDLE); diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index 3d930d0cb..2748b1221 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -701,11 +701,12 @@ struct VkResourceRecord : public ResourceRecord struct MemState { MemState() - : mapOffset(0), mapSize(0), size(0), mapFlags(0), mappedPtr(0) + : mapOffset(0), mapSize(0), size(0), mapFlags(0), mappedPtr(0), mapFlushed(false) { } VkDeviceSize mapOffset, mapSize; VkDeviceSize size; VkMemoryMapFlags mapFlags; + bool mapFlushed; void *mappedPtr; }; struct ImgState diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index c9cbe1c64..3cd2e9fd1 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -145,6 +145,7 @@ VkResult WrappedVulkan::vkMapMemory( it->second.mapOffset = offset; it->second.mapSize = size == 0 ? it->second.size : size; it->second.mapFlags = flags; + it->second.mapFlushed = false; } } else if(m_State >= WRITING) @@ -182,8 +183,11 @@ bool WrappedVulkan::Serialise_vkUnmapMemory( device = GetResourceManager()->GetLiveHandle(devId); mem = GetResourceManager()->GetLiveHandle(id); + // VKTODOLOW figure out what alignments there are on mapping, so we only map the region + // we're going to modify. For no, offset/size is handled in the memcpy before and we + // map the whole region void *mapPtr = NULL; - VkResult ret = ObjDisp(device)->MapMemory(Unwrap(device), Unwrap(mem), memOffset, memSize, flags, &mapPtr); + VkResult ret = ObjDisp(device)->MapMemory(Unwrap(device), Unwrap(mem), 0, 0, flags, &mapPtr); if(ret != VK_SUCCESS) { @@ -226,19 +230,27 @@ VkResult WrappedVulkan::vkUnmapMemory( { if(ret == VK_SUCCESS && m_State >= WRITING_CAPFRAME) { - SCOPED_SERIALISE_CONTEXT(UNMAP_MEM); - Serialise_vkUnmapMemory(device, mem); - - VkResourceRecord *record = GetRecord(mem); - - if(m_State == WRITING_IDLE) + if(!it->second.mapFlushed) { - record->AddChunk(scope.Get()); + SCOPED_SERIALISE_CONTEXT(UNMAP_MEM); + Serialise_vkUnmapMemory(device, mem); + + VkResourceRecord *record = GetRecord(mem); + + if(m_State == WRITING_IDLE) + { + record->AddChunk(scope.Get()); + } + else + { + m_FrameCaptureRecord->AddChunk(scope.Get()); + GetResourceManager()->MarkResourceFrameReferenced(GetResID(mem), eFrameRef_Write); + } } else { - m_FrameCaptureRecord->AddChunk(scope.Get()); - GetResourceManager()->MarkResourceFrameReferenced(GetResID(mem), eFrameRef_Write); + // VKTODOLOW for now assuming flushes cover all writes. Technically + // this is true for all non-coherent memory types. } } else @@ -254,6 +266,59 @@ VkResult WrappedVulkan::vkUnmapMemory( return ret; } +bool WrappedVulkan::Serialise_vkFlushMappedMemoryRanges( + VkDevice device, + uint32_t memRangeCount, + const VkMappedMemoryRange* pMemRanges) +{ + SERIALISE_ELEMENT(ResourceId, devId, GetResID(device)); + SERIALISE_ELEMENT(ResourceId, id, GetResID(pMemRanges->mem)); + + auto it = m_MemoryInfo.find(id); + + SERIALISE_ELEMENT(VkMemoryMapFlags, flags, it->second.mapFlags); + SERIALISE_ELEMENT(uint64_t, memOffset, pMemRanges->offset); + SERIALISE_ELEMENT(uint64_t, memSize, pMemRanges->size); + + // VKTODOHIGH: this is really horrible - this could be write-combined memory that we're + // reading from to get the latest data. This saves on having to fetch the data some + // other way and provide an interception buffer to the app, but is awful. + // we're also not doing any diff range checks, just serialising the whole memory region. + // In vulkan the common case will be one memory region for a large number of distinct + // bits of data so most maps will not change the whole region. + SERIALISE_ELEMENT_BUF(byte*, data, (byte *)it->second.mappedPtr + (size_t)memOffset, (size_t)memSize); + + if(m_State < WRITING) + { + device = GetResourceManager()->GetLiveHandle(devId); + VkDeviceMemory mem = GetResourceManager()->GetLiveHandle(id); + + // VKTODOLOW figure out what alignments there are on mapping, so we only map the region + // we're going to modify. For no, offset/size is handled in the memcpy before and we + // map the whole region + void *mapPtr = NULL; + VkResult ret = ObjDisp(device)->MapMemory(Unwrap(device), Unwrap(mem), 0, 0, flags, &mapPtr); + + if(ret != VK_SUCCESS) + { + RDCERR("Error mapping memory on replay: 0x%08x", ret); + } + else + { + memcpy((byte *)mapPtr+memOffset, data, (size_t)memSize); + + ret = ObjDisp(device)->UnmapMemory(Unwrap(device), Unwrap(mem)); + + if(ret != VK_SUCCESS) + RDCERR("Error unmapping memory on replay: 0x%08x", ret); + } + + SAFE_DELETE_ARRAY(data); + } + + return true; +} + VkResult WrappedVulkan::vkFlushMappedMemoryRanges( VkDevice device, uint32_t memRangeCount, @@ -272,7 +337,17 @@ VkResult WrappedVulkan::vkFlushMappedMemoryRanges( for(uint32_t i=0; i < memRangeCount; i++) { - RDCDEBUG("FlushMemory(%p, %llu, %llu)", pMemRanges[i].mem, pMemRanges[i].offset, pMemRanges[i].size); + auto it = m_MemoryInfo.find(GetResID(pMemRanges[i].mem)); + it->second.mapFlushed = true; + + if(ret == VK_SUCCESS && m_State >= WRITING_CAPFRAME && !it->second.mapFlushed) + { + SCOPED_SERIALISE_CONTEXT(FLUSH_MEM); + Serialise_vkFlushMappedMemoryRanges(device, 1, pMemRanges+i); + + m_FrameCaptureRecord->AddChunk(scope.Get()); + GetResourceManager()->MarkResourceFrameReferenced(GetResID(pMemRanges[i].mem), eFrameRef_Write); + } } return ret;