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
This commit is contained in:
baldurk
2015-09-21 13:45:34 +02:00
parent 2cfa89ee18
commit b6c7a406c1
4 changed files with 93 additions and 12 deletions
+1
View File
@@ -148,6 +148,7 @@ enum VulkanChunkType
ALLOC_MEM,
UNMAP_MEM,
FLUSH_MEM,
FREE_MEM,
CREATE_CMD_POOL,
+4
View File
@@ -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);
+2 -1
View File
@@ -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
@@ -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<VkDevice>(devId);
mem = GetResourceManager()->GetLiveHandle<VkDeviceMemory>(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<VkDevice>(devId);
VkDeviceMemory mem = GetResourceManager()->GetLiveHandle<VkDeviceMemory>(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;