diff --git a/renderdoc/driver/vulkan/CMakeLists.txt b/renderdoc/driver/vulkan/CMakeLists.txt index c3c4f5196..ca36d437a 100644 --- a/renderdoc/driver/vulkan/CMakeLists.txt +++ b/renderdoc/driver/vulkan/CMakeLists.txt @@ -26,7 +26,6 @@ set(sources vk_info.cpp vk_info.h vk_initstate.cpp - vk_sparse_initstate.cpp vk_manager.cpp vk_manager.h vk_memory.cpp diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj index 41e12b7ec..94de6126d 100644 --- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj +++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj @@ -124,7 +124,6 @@ - diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters index 8077da456..3a4ab9418 100644 --- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters +++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters @@ -106,9 +106,6 @@ Util - - Core - Replay diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 0581fa305..c4e0ef859 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -344,6 +344,10 @@ bool VkInitParams::IsSupportedVersion(uint64_t ver) if(ver == CurrentVersion) return true; + // 0x12 -> 0x13 - added full sparse resource support + if(ver == 0x12) + return true; + // 0x11 -> 0x12 - added inline uniform block support if(ver == 0x11) return true; diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index a2f7e72ea..17a8aa995 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -53,7 +53,7 @@ struct VkInitParams uint64_t GetSerialiseSize(); // check if a frame capture section version is supported - static const uint64_t CurrentVersion = 0x12; + static const uint64_t CurrentVersion = 0x13; static bool IsSupportedVersion(uint64_t ver); }; diff --git a/renderdoc/driver/vulkan/vk_initstate.cpp b/renderdoc/driver/vulkan/vk_initstate.cpp index a47b798ae..2af7218c9 100644 --- a/renderdoc/driver/vulkan/vk_initstate.cpp +++ b/renderdoc/driver/vulkan/vk_initstate.cpp @@ -26,6 +26,13 @@ #include "vk_core.h" #include "vk_debug.h" +template +void DoSerialise(SerialiserType &ser, MemIDOffset &el) +{ + SERIALISE_MEMBER(memory); + SERIALISE_MEMBER(memOffs); +} + // VKTODOLOW there's a lot of duplicated code in this file for creating a buffer to do // a memory copy and saving to disk. @@ -72,7 +79,7 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res) // buffers are only dirty if they are sparse RDCASSERT(buffer->record->resInfo && buffer->record->resInfo->IsSparse()); - return Prepare_SparseInitialState(buffer); + return true; } else if(type == eResImage) { @@ -84,9 +91,7 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res) if(resInfo.IsSparse()) { - // if the image is sparse we have to do a different kind of initial state prepare, - // to serialise out the page mapping. The fetching of memory is also different - return Prepare_SparseInitialState((WrappedVkImage *)res); + // if the image is sparse we have to snapshot the page table } LockedImageStateRef state = FindImageState(im->id); @@ -535,13 +540,10 @@ uint64_t WrappedVulkan::GetSize_InitialState(ResourceId id, const VkInitialConte else if(initial.type == eResBuffer) { // buffers only have initial states when they're sparse - return GetSize_SparseInitialState(id, initial); + return 0; } else if(initial.type == eResImage || initial.type == eResDeviceMemory) { - if(initial.tag == VkInitialContents::Sparse) - return GetSize_SparseInitialState(id, initial); - // the size primarily comes from the buffer, the size of which we conveniently have stored. return uint64_t(128 + initial.mem.size + WriteSerialiser::GetChunkAlignment()); } @@ -897,8 +899,36 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V } else if(type == eResBuffer) { - // buffers only have initial states when they're sparse - return Serialise_SparseBufferInitialState(ser, id, initial); + if(ser.IsReading() && ser.VersionLess(0x13)) + { + RDCWARN( + "Skipping sparse initial states of buffer from old capture. " + "Please re-capture with this version of RenderDoc."); + + // serialise without allocating, this makes for a skip + VkSparseMemoryBind *binds = NULL; + ser.Serialise("binds"_lit, binds, 0, SerialiserFlags::NoFlags).Hidden(); + uint32_t numBinds = 0; + SERIALISE_ELEMENT(numBinds).Hidden(); + + MemIDOffset *memDataOffs = NULL; + ser.Serialise("memDataOffs"_lit, memDataOffs, 0, SerialiserFlags::NoFlags).Hidden(); + uint32_t numUniqueMems = 0; + SERIALISE_ELEMENT(numUniqueMems).Hidden(); + + VkDeviceSize totalSize = 0; + SERIALISE_ELEMENT(totalSize).Hidden(); + + uint64_t ContentsSize = 0; + SERIALISE_ELEMENT(ContentsSize).Hidden(); + + byte *Contents = NULL; + ser.Serialise("Contents"_lit, Contents, ContentsSize, SerialiserFlags::NoFlags).Hidden(); + + SERIALISE_CHECK_READ_ERRORS(); + + return true; + } } else if(type == eResDeviceMemory || type == eResImage) { @@ -906,23 +936,64 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V // if we have a blob of data, this contains sparse mapping so re-direct to the sparse // implementation of this function - SERIALISE_ELEMENT_LOCAL(IsSparse, initial && initial->tag == VkInitialContents::Sparse); + SERIALISE_ELEMENT_LOCAL(IsSparse, false); if(IsSparse) { - ret = false; - if(type == eResImage) { - ret = Serialise_SparseImageInitialState(ser, id, initial); + if(ser.IsReading() && ser.VersionLess(0x13)) + { + RDCWARN( + "Skipping sparse initial states of buffer from old capture. " + "Please re-capture with this version of RenderDoc."); + + // serialise without allocating, this makes for a skip + VkSparseMemoryBind *opaque = NULL; + ser.Serialise("opaque"_lit, opaque, 0, SerialiserFlags::NoFlags).Hidden(); + uint32_t opaqueCount = 0; + SERIALISE_ELEMENT(opaqueCount).Hidden(); + + VkExtent3D imgdim = {}; + SERIALISE_ELEMENT(imgdim).Hidden(); + + VkExtent3D pagedim = {}; + SERIALISE_ELEMENT(pagedim).Hidden(); + + static const uint32_t numLegacyAspects = 4; + for(uint32_t a = 0; a < numLegacyAspects; a++) + { + MemIDOffset *pages = NULL; + ser.Serialise("pages"_lit, pages, 0, SerialiserFlags::NoFlags).Hidden(); + } + + uint32_t pageCount[numLegacyAspects] = {}; + SERIALISE_ELEMENT(pageCount).Hidden(); + + MemIDOffset *memDataOffs = NULL; + ser.Serialise("memDataOffs"_lit, memDataOffs, 0, SerialiserFlags::NoFlags).Hidden(); + uint32_t numUniqueMems = 0; + SERIALISE_ELEMENT(numUniqueMems).Hidden(); + + VkDeviceSize totalSize = 0; + SERIALISE_ELEMENT(totalSize).Hidden(); + + uint64_t ContentsSize = 0; + SERIALISE_ELEMENT(ContentsSize).Hidden(); + + byte *Contents = NULL; + ser.Serialise("Contents"_lit, Contents, ContentsSize, SerialiserFlags::NoFlags).Hidden(); + + SERIALISE_CHECK_READ_ERRORS(); + + return true; + } } else { RDCERR("Invalid initial state - sparse marker for device memory"); - ret = false; + return false; } - - return ret; } VkResult vkr = VK_SUCCESS; @@ -1352,7 +1423,6 @@ void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, const VkInitialConten } else if(type == eResBuffer) { - Apply_SparseInitialState((WrappedVkBuffer *)live, initial); } else if(type == eResImage) { @@ -1413,12 +1483,6 @@ void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, const VkInitialConten } } - if(initial.tag == VkInitialContents::Sparse) - { - Apply_SparseInitialState((WrappedVkImage *)live, initial); - return; - } - // handle any 'created' initial states, without an actual image with contents if(initial.tag != VkInitialContents::BufferCopy) { diff --git a/renderdoc/driver/vulkan/vk_manager.h b/renderdoc/driver/vulkan/vk_manager.h index 116dd5f24..914e4a7db 100644 --- a/renderdoc/driver/vulkan/vk_manager.h +++ b/renderdoc/driver/vulkan/vk_manager.h @@ -37,43 +37,6 @@ struct MemIDOffset DECLARE_REFLECTION_STRUCT(MemIDOffset); -struct SparseBufferInitState -{ - VkSparseMemoryBind *binds; - uint32_t numBinds; - - MemIDOffset *memDataOffs; - uint32_t numUniqueMems; - - VkDeviceSize totalSize; -}; - -DECLARE_REFLECTION_STRUCT(SparseBufferInitState); - -struct SparseImageInitState -{ - VkSparseMemoryBind *opaque; - uint32_t opaqueCount; - - VkExtent3D imgdim; // in pages - VkExtent3D pagedim; - - // available on capture - filled out in Prepare_SparseInitialState and serialised to disk - MemIDOffset *pages[NUM_VK_IMAGE_ASPECTS]; - - uint32_t pageCount[NUM_VK_IMAGE_ASPECTS]; - - // available on replay - filled out in the read path of Serialise_SparseInitialState - VkSparseImageMemoryBind *pageBinds[NUM_VK_IMAGE_ASPECTS]; - - MemIDOffset *memDataOffs; - uint32_t numUniqueMems; - - VkDeviceSize totalSize; -}; - -DECLARE_REFLECTION_STRUCT(SparseImageInitState); - // this struct is copied around and for that reason we explicitly keep it simple and POD. The // lifetime of the memory allocated is controlled by the resource manager - when preparing or // serialising, we explicitly set the initial contents, then when the whole system is done with them @@ -85,7 +48,6 @@ struct VkInitialContents BufferCopy = 0, ClearColorImage = 1, ClearDepthStencilImage, - Sparse, DescriptorSet, }; @@ -124,25 +86,6 @@ struct VkInitialContents rm->ResourceTypeRelease(GetWrapped(img)); // memory is not free'd here - - if(tag == Sparse) - { - if(type == eResImage) - { - SAFE_DELETE_ARRAY(sparseImage.opaque); - for(uint32_t i = 0; i < NUM_VK_IMAGE_ASPECTS; i++) - { - SAFE_DELETE_ARRAY(sparseImage.pages[i]); - SAFE_DELETE_ARRAY(sparseImage.pageBinds[i]); - } - SAFE_DELETE_ARRAY(sparseImage.memDataOffs); - } - else if(type == eResBuffer) - { - SAFE_DELETE_ARRAY(sparseBuffer.binds); - SAFE_DELETE_ARRAY(sparseBuffer.memDataOffs); - } - } } // for descriptor heaps, when capturing we save the slots, when replaying we store direct writes @@ -160,13 +103,6 @@ struct VkInitialContents VkImage img; MemoryAllocation mem; Tag tag; - - // sparse resources need extra information. Which one is valid, depends on the value of type above - union - { - SparseBufferInitState sparseBuffer; - SparseImageInitState sparseImage; - }; }; struct VulkanResourceManagerConfiguration diff --git a/renderdoc/driver/vulkan/vk_sparse_initstate.cpp b/renderdoc/driver/vulkan/vk_sparse_initstate.cpp deleted file mode 100644 index 844a49e73..000000000 --- a/renderdoc/driver/vulkan/vk_sparse_initstate.cpp +++ /dev/null @@ -1,896 +0,0 @@ -/****************************************************************************** - * The MIT License (MIT) - * - * Copyright (c) 2019-2021 Baldur Karlsson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -#include "vk_core.h" -#include "vk_debug.h" - -template -void DoSerialise(SerialiserType &ser, MemIDOffset &el) -{ - SERIALISE_MEMBER(memory); - SERIALISE_MEMBER(memOffs); -} - -template -void DoSerialise(SerialiserType &ser, SparseBufferInitState &el) -{ - SERIALISE_MEMBER_ARRAY(binds, numBinds); - SERIALISE_MEMBER(numBinds); - SERIALISE_MEMBER_ARRAY(memDataOffs, numUniqueMems); - SERIALISE_MEMBER(numUniqueMems); - SERIALISE_MEMBER(totalSize); -} - -template <> -void Deserialise(const SparseBufferInitState &el) -{ - delete[] el.binds; - delete[] el.memDataOffs; -} - -template -void DoSerialise(SerialiserType &ser, SparseImageInitState &el) -{ - SERIALISE_MEMBER_ARRAY(opaque, opaqueCount); - SERIALISE_MEMBER(opaqueCount); - SERIALISE_MEMBER(imgdim); - SERIALISE_MEMBER(pagedim); - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - SERIALISE_MEMBER_ARRAY(pages[a], pageCount[a]); - SERIALISE_MEMBER(pageCount); - SERIALISE_MEMBER_ARRAY(memDataOffs, numUniqueMems); - SERIALISE_MEMBER(numUniqueMems); - SERIALISE_MEMBER(totalSize); -} - -template <> -void Deserialise(const SparseImageInitState &el) -{ - delete[] el.opaque; - delete[] el.memDataOffs; - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - delete[] el.pages[a]; -} - -bool WrappedVulkan::Prepare_SparseInitialState(WrappedVkBuffer *buf) -{ - ResourceId id = buf->id; - - // VKTODOLOW this is a bit conservative, as we save the whole memory object rather than just the - // bound range. - std::map boundMems; - - // value will be filled out later once all memories are added - for(size_t i = 0; i < buf->record->resInfo->opaquemappings.size(); i++) - boundMems[buf->record->resInfo->opaquemappings[i].memory] = 0; - - uint32_t numElems = (uint32_t)buf->record->resInfo->opaquemappings.size(); - - VkInitialContents initContents; - - initContents.tag = VkInitialContents::Sparse; - initContents.type = eResBuffer; - - initContents.sparseBuffer.numBinds = numElems; - initContents.sparseBuffer.binds = new VkSparseMemoryBind[numElems]; - initContents.sparseBuffer.numUniqueMems = (uint32_t)boundMems.size(); - initContents.sparseBuffer.memDataOffs = new MemIDOffset[boundMems.size()]; - - memcpy(initContents.sparseBuffer.binds, &buf->record->resInfo->opaquemappings[0], - sizeof(VkSparseMemoryBind) * numElems); - - VkDevice d = GetDev(); - // INITSTATEBATCH - VkCommandBuffer cmd = GetNextCmd(); - - VkBufferCreateInfo bufInfo = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - NULL, - 0, - 0, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - }; - - uint32_t memidx = 0; - for(auto it = boundMems.begin(); it != boundMems.end(); ++it) - { - // store offset - it->second = bufInfo.size; - - initContents.sparseBuffer.memDataOffs[memidx].memory = GetResID(it->first); - initContents.sparseBuffer.memDataOffs[memidx].memOffs = bufInfo.size; - - // increase size - bufInfo.size += GetRecord(it->first)->Length; - memidx++; - } - - initContents.sparseBuffer.totalSize = bufInfo.size; - - // since this happens during capture, we don't want to start serialising extra buffer creates, so - // we manually create & then just wrap. - VkBuffer dstBuf; - - VkResult vkr = VK_SUCCESS; - - vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &dstBuf); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - GetResourceManager()->WrapResource(Unwrap(d), dstBuf); - - MemoryAllocation readbackmem = - AllocateMemoryForResource(dstBuf, MemoryScope::InitialContents, MemoryType::Readback); - - initContents.mem = readbackmem; - - vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(dstBuf), Unwrap(readbackmem.mem), - readbackmem.offs); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - rdcarray bufdeletes; - bufdeletes.push_back(dstBuf); - - VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; - - vkr = ObjDisp(d)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - // copy all of the bound memory objects - for(auto it = boundMems.begin(); it != boundMems.end(); ++it) - { - VkBuffer srcBuf; - - bufInfo.size = GetRecord(it->first)->Length; - vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &srcBuf); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - GetResourceManager()->WrapResource(Unwrap(d), srcBuf); - - vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(srcBuf), Unwrap(it->first), 0); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - // copy srcbuf into its area in dstbuf - VkBufferCopy region = {0, it->second, bufInfo.size}; - - ObjDisp(d)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(dstBuf), 1, ®ion); - - bufdeletes.push_back(srcBuf); - } - - vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd)); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - // INITSTATEBATCH - SubmitCmds(); - FlushQ(); - - for(size_t i = 0; i < bufdeletes.size(); i++) - { - ObjDisp(d)->DestroyBuffer(Unwrap(d), Unwrap(bufdeletes[i]), NULL); - GetResourceManager()->ReleaseWrappedResource(bufdeletes[i]); - } - - GetResourceManager()->SetInitialContents(id, initContents); - - return true; -} - -bool WrappedVulkan::Prepare_SparseInitialState(WrappedVkImage *im) -{ - ResourceId id = im->id; - - ResourceInfo *sparse = im->record->resInfo; - - // VKTODOLOW this is a bit conservative, as we save the whole memory object rather than just the - // bound range. - std::map boundMems; - - // value will be filled out later once all memories are added - for(size_t i = 0; i < sparse->opaquemappings.size(); i++) - boundMems[sparse->opaquemappings[i].memory] = 0; - - uint32_t pagePerAspect = sparse->imgdim.width * sparse->imgdim.height * sparse->imgdim.depth; - - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - { - if(sparse->pages[a]) - { - for(uint32_t i = 0; i < pagePerAspect; i++) - if(sparse->pages[a][i].first != VK_NULL_HANDLE) - boundMems[sparse->pages[a][i].first] = 0; - } - } - - uint32_t opaqueCount = (uint32_t)sparse->opaquemappings.size(); - - VkInitialContents initContents; - - initContents.tag = VkInitialContents::Sparse; - initContents.type = eResImage; - - SparseImageInitState &sparseInit = initContents.sparseImage; - - sparseInit.opaqueCount = opaqueCount; - sparseInit.opaque = new VkSparseMemoryBind[opaqueCount]; - sparseInit.imgdim = sparse->imgdim; - sparseInit.pagedim = sparse->pagedim; - sparseInit.numUniqueMems = (uint32_t)boundMems.size(); - sparseInit.memDataOffs = new MemIDOffset[boundMems.size()]; - - if(opaqueCount > 0) - memcpy(sparseInit.opaque, &sparse->opaquemappings[0], sizeof(VkSparseMemoryBind) * opaqueCount); - - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - { - sparseInit.pageCount[a] = (sparse->pages[a] ? pagePerAspect : 0); - - if(sparseInit.pageCount[a] != 0) - { - sparseInit.pages[a] = new MemIDOffset[pagePerAspect]; - - for(uint32_t i = 0; i < pagePerAspect; i++) - { - sparseInit.pages[a][i].memory = GetResID(sparse->pages[a][i].first); - sparseInit.pages[a][i].memOffs = sparse->pages[a][i].second; - } - } - else - { - sparseInit.pages[a] = NULL; - } - } - - VkDevice d = GetDev(); - // INITSTATEBATCH - VkCommandBuffer cmd = GetNextCmd(); - - VkBufferCreateInfo bufInfo = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - NULL, - 0, - 0, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - }; - - uint32_t memidx = 0; - for(auto it = boundMems.begin(); it != boundMems.end(); ++it) - { - // store offset - it->second = bufInfo.size; - - sparseInit.memDataOffs[memidx].memory = GetResID(it->first); - sparseInit.memDataOffs[memidx].memOffs = bufInfo.size; - - // increase size - bufInfo.size += GetRecord(it->first)->Length; - memidx++; - } - - sparseInit.totalSize = bufInfo.size; - - // since this happens during capture, we don't want to start serialising extra buffer creates, so - // we manually create & then just wrap. - VkBuffer dstBuf; - - VkResult vkr = VK_SUCCESS; - - vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &dstBuf); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - GetResourceManager()->WrapResource(Unwrap(d), dstBuf); - - MemoryAllocation readbackmem = - AllocateMemoryForResource(dstBuf, MemoryScope::InitialContents, MemoryType::Readback); - - initContents.mem = readbackmem; - - vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(dstBuf), Unwrap(readbackmem.mem), - readbackmem.offs); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - rdcarray bufdeletes; - bufdeletes.push_back(dstBuf); - - VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; - - vkr = ObjDisp(d)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - // copy all of the bound memory objects - for(auto it = boundMems.begin(); it != boundMems.end(); ++it) - { - VkBuffer srcBuf; - - bufInfo.size = GetRecord(it->first)->Length; - vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &srcBuf); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - GetResourceManager()->WrapResource(Unwrap(d), srcBuf); - - vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(srcBuf), Unwrap(it->first), 0); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - // copy srcbuf into its area in dstbuf - VkBufferCopy region = {0, it->second, bufInfo.size}; - - ObjDisp(d)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(dstBuf), 1, ®ion); - - bufdeletes.push_back(srcBuf); - } - - vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd)); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - // INITSTATEBATCH - SubmitCmds(); - FlushQ(); - - for(size_t i = 0; i < bufdeletes.size(); i++) - { - ObjDisp(d)->DestroyBuffer(Unwrap(d), Unwrap(bufdeletes[i]), NULL); - GetResourceManager()->ReleaseWrappedResource(bufdeletes[i]); - } - - GetResourceManager()->SetInitialContents(id, initContents); - - return true; -} - -uint64_t WrappedVulkan::GetSize_SparseInitialState(ResourceId id, const VkInitialContents &initial) -{ - if(initial.type == eResBuffer) - { - const SparseBufferInitState &info = initial.sparseBuffer; - - // some bytes just to cover overheads etc. - uint64_t ret = 128; - - // the list of memory objects bound - ret += 8 + sizeof(VkSparseMemoryBind) * info.numBinds; - - // the list of memory regions to copy - ret += 8 + sizeof(MemIDOffset) * info.numUniqueMems; - - // the actual data - ret += uint64_t(info.totalSize + WriteSerialiser::GetChunkAlignment()); - - return ret; - } - else if(initial.type == eResImage) - { - const SparseImageInitState &info = initial.sparseImage; - - // some bytes just to cover overheads etc. - uint64_t ret = 128; - - // the meta-data structure - ret += sizeof(SparseImageInitState); - - // the list of memory objects bound - ret += sizeof(VkSparseMemoryBind) * info.opaqueCount; - - // the page tables - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - ret += 8 + sizeof(MemIDOffset) * info.pageCount[a]; - - // the list of memory regions to copy - ret += sizeof(MemIDOffset) * info.numUniqueMems; - - // the actual data - ret += uint64_t(info.totalSize + WriteSerialiser::GetChunkAlignment()); - - return ret; - } - - RDCERR("Unhandled resource type %s", ToStr(initial.type).c_str()); - return 128; -} - -template -bool WrappedVulkan::Serialise_SparseBufferInitialState(SerialiserType &ser, ResourceId id, - const VkInitialContents *contents) -{ - VkDevice d = !IsStructuredExporting(m_State) ? GetDev() : VK_NULL_HANDLE; - VkResult vkr = VK_SUCCESS; - - SERIALISE_ELEMENT_LOCAL(SparseState, contents->sparseBuffer); - - MemoryAllocation mappedMem; - byte *Contents = NULL; - uint64_t ContentsSize = (uint64_t)SparseState.totalSize; - - // Serialise this separately so that it can be used on reading to prepare the upload memory - SERIALISE_ELEMENT(ContentsSize); - - // the memory/buffer that we allocated on read, to upload the initial contents. - MemoryAllocation uploadMemory; - VkBuffer uploadBuf = VK_NULL_HANDLE; - - // during writing, we already have the memory copied off - we just need to map it. - if(ser.IsWriting()) - { - // the memory was created not wrapped. - mappedMem = contents->mem; - vkr = ObjDisp(d)->MapMemory(Unwrap(d), Unwrap(mappedMem.mem), mappedMem.offs, mappedMem.size, 0, - (void **)&Contents); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - // invalidate the cpu cache for this memory range to avoid reading stale data - VkMappedMemoryRange range = { - VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, - NULL, - Unwrap(mappedMem.mem), - mappedMem.offs, - mappedMem.size, - }; - - vkr = ObjDisp(d)->InvalidateMappedMemoryRanges(Unwrap(d), 1, &range); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - } - else if(IsReplayingAndReading() && !ser.IsErrored()) - { - // create a buffer with memory attached, which we will fill with the initial contents - VkBufferCreateInfo bufInfo = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - NULL, - 0, - ContentsSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - }; - - vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &uploadBuf); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - GetResourceManager()->WrapResource(Unwrap(d), uploadBuf); - - uploadMemory = - AllocateMemoryForResource(uploadBuf, MemoryScope::InitialContents, MemoryType::Upload); - - vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(uploadBuf), Unwrap(uploadMemory.mem), - uploadMemory.offs); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - mappedMem = uploadMemory; - - ObjDisp(d)->MapMemory(Unwrap(d), Unwrap(uploadMemory.mem), uploadMemory.offs, uploadMemory.size, - 0, (void **)&Contents); - } - - // not using SERIALISE_ELEMENT_ARRAY so we can deliberately avoid allocation - we serialise - // directly into upload memory - ser.Serialise("Contents"_lit, Contents, ContentsSize, SerialiserFlags::NoFlags); - - // unmap the resource we mapped before - we need to do this on read and on write. - if(!IsStructuredExporting(m_State) && mappedMem.mem != VK_NULL_HANDLE) - { - if(IsReplayingAndReading()) - { - // first ensure we flush the writes from the cpu to gpu memory - VkMappedMemoryRange range = { - VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, - NULL, - Unwrap(mappedMem.mem), - mappedMem.offs, - mappedMem.size, - }; - - vkr = ObjDisp(d)->FlushMappedMemoryRanges(Unwrap(d), 1, &range); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - } - - ObjDisp(d)->UnmapMemory(Unwrap(d), Unwrap(mappedMem.mem)); - } - - SERIALISE_CHECK_READ_ERRORS(); - - if(IsReplayingAndReading()) - { - VkInitialContents initContents; - initContents.type = eResBuffer; - initContents.buf = uploadBuf; - initContents.mem = uploadMemory; - initContents.tag = VkInitialContents::Sparse; - initContents.sparseBuffer = SparseState; - - // we steal the serialised arrays here by resetting the struct, then the serialisation won't - // deallocate them. VkInitialContents::Free() will deallocate them in the same way. - SparseState = SparseBufferInitState(); - - GetResourceManager()->SetInitialContents(id, initContents); - } - - return true; -} - -template -bool WrappedVulkan::Serialise_SparseImageInitialState(SerialiserType &ser, ResourceId id, - const VkInitialContents *contents) -{ - VkDevice d = !IsStructuredExporting(m_State) ? GetDev() : VK_NULL_HANDLE; - VkResult vkr = VK_SUCCESS; - - SERIALISE_ELEMENT_LOCAL(SparseState, contents->sparseImage); - - MemoryAllocation mappedMem; - byte *Contents = NULL; - uint64_t ContentsSize = (uint64_t)SparseState.totalSize; - - // Serialise this separately so that it can be used on reading to prepare the upload memory - SERIALISE_ELEMENT(ContentsSize); - - // the memory/buffer that we allocated on read, to upload the initial contents. - MemoryAllocation uploadMemory; - VkBuffer uploadBuf = VK_NULL_HANDLE; - - // during writing, we already have the memory copied off - we just need to map it. - if(ser.IsWriting()) - { - mappedMem = contents->mem; - vkr = ObjDisp(d)->MapMemory(Unwrap(d), Unwrap(mappedMem.mem), mappedMem.offs, mappedMem.size, 0, - (void **)&Contents); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - } - else if(IsReplayingAndReading() && !ser.IsErrored()) - { - // create a buffer with memory attached, which we will fill with the initial contents - VkBufferCreateInfo bufInfo = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - NULL, - 0, - ContentsSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - }; - - vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &uploadBuf); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - GetResourceManager()->WrapResource(Unwrap(d), uploadBuf); - - uploadMemory = - AllocateMemoryForResource(uploadBuf, MemoryScope::InitialContents, MemoryType::Upload); - - vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(uploadBuf), Unwrap(uploadMemory.mem), - uploadMemory.offs); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - mappedMem = uploadMemory; - - ObjDisp(d)->MapMemory(Unwrap(d), Unwrap(uploadMemory.mem), uploadMemory.offs, uploadMemory.size, - 0, (void **)&Contents); - } - - // not using SERIALISE_ELEMENT_ARRAY so we can deliberately avoid allocation - we serialise - // directly into upload memory - ser.Serialise("Contents"_lit, Contents, ContentsSize, SerialiserFlags::NoFlags); - - // unmap the resource we mapped before - we need to do this on read and on write. - if(!IsStructuredExporting(m_State) && mappedMem.mem != VK_NULL_HANDLE) - ObjDisp(d)->UnmapMemory(Unwrap(d), Unwrap(mappedMem.mem)); - - SERIALISE_CHECK_READ_ERRORS(); - - if(IsReplayingAndReading()) - { - VkInitialContents initContents; - initContents.type = eResImage; - initContents.buf = uploadBuf; - initContents.mem = uploadMemory; - initContents.tag = VkInitialContents::Sparse; - initContents.sparseImage = SparseState; - - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - { - if(SparseState.pageCount[a] == 0) - { - initContents.sparseImage.pageBinds[a] = NULL; - } - else - { - initContents.sparseImage.pageBinds[a] = new VkSparseImageMemoryBind[SparseState.pageCount[a]]; - - uint32_t i = 0; - - for(uint32_t z = 0; z < SparseState.imgdim.depth; z++) - { - for(uint32_t y = 0; y < SparseState.imgdim.height; y++) - { - for(uint32_t x = 0; x < SparseState.imgdim.width; x++) - { - VkSparseImageMemoryBind &p = initContents.sparseImage.pageBinds[a][i]; - - p.memory = Unwrap(GetResourceManager()->GetLiveHandle( - SparseState.pages[a][i].memory)); - p.memoryOffset = SparseState.pages[a][i].memOffs; - p.extent = SparseState.pagedim; - p.subresource.aspectMask = (VkImageAspectFlags)(1 << a); - p.subresource.arrayLayer = 0; - p.subresource.mipLevel = 0; - p.offset.x = x * p.extent.width; - p.offset.y = y * p.extent.height; - p.offset.z = z * p.extent.depth; - - i++; - } - } - } - } - } - - // delete and free the pages array, we no longer need it. - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - SAFE_DELETE_ARRAY(SparseState.pages[a]); - - // we steal the serialised arrays here by resetting the struct, then the serialisation won't - // deallocate them. VkInitialContents::Free() will deallocate them in the same way. - SparseState = SparseImageInitState(); - - GetResourceManager()->SetInitialContents(id, initContents); - } - - return true; -} - -template bool WrappedVulkan::Serialise_SparseBufferInitialState(ReadSerialiser &ser, ResourceId id, - const VkInitialContents *contents); -template bool WrappedVulkan::Serialise_SparseBufferInitialState(WriteSerialiser &ser, ResourceId id, - const VkInitialContents *contents); -template bool WrappedVulkan::Serialise_SparseImageInitialState(ReadSerialiser &ser, ResourceId id, - const VkInitialContents *contents); -template bool WrappedVulkan::Serialise_SparseImageInitialState(WriteSerialiser &ser, ResourceId id, - const VkInitialContents *contents); - -bool WrappedVulkan::Apply_SparseInitialState(WrappedVkBuffer *buf, const VkInitialContents &contents) -{ - const SparseBufferInitState &info = contents.sparseBuffer; - - // unbind the entire buffer so that any new areas that are bound are unbound again - - VkQueue q = GetQ(); - - VkMemoryRequirements mrq = {}; - ObjDisp(q)->GetBufferMemoryRequirements(Unwrap(GetDev()), buf->real.As(), &mrq); - - VkSparseMemoryBind unbind = {0, RDCMAX(mrq.size, m_CreationInfo.m_Buffer[buf->id].size), - VK_NULL_HANDLE, 0, 0}; - - VkSparseBufferMemoryBindInfo bufBind = {buf->real.As(), 1, &unbind}; - - // this semaphore separates the unbind and bind, as there isn't an ordering guarantee - // for two adjacent batches that bind the same resource. - VkSemaphore sem = GetNextSemaphore(); - - VkBindSparseInfo bindsparse = { - VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, - NULL, - 0, - NULL, // wait semaphores - 1, - &bufBind, - 0, - NULL, // image opaque - 0, - NULL, // image bind - 1, - UnwrapPtr(sem), // signal semaphores - }; - - // first unbind all - ObjDisp(q)->QueueBindSparse(Unwrap(q), 1, &bindsparse, VK_NULL_HANDLE); - - // then make any bindings - if(info.numBinds > 0) - { - bufBind.bindCount = info.numBinds; - bufBind.pBinds = info.binds; - - // wait for unbind semaphore - bindsparse.waitSemaphoreCount = 1; - bindsparse.pWaitSemaphores = bindsparse.pSignalSemaphores; - - bindsparse.signalSemaphoreCount = 0; - bindsparse.pSignalSemaphores = NULL; - - ObjDisp(q)->QueueBindSparse(Unwrap(q), 1, &bindsparse, VK_NULL_HANDLE); - } - - // marks that the above semaphore has been used, so next time we - // flush it will be moved back to the pool - SubmitSemaphores(); - - VkResult vkr = VK_SUCCESS; - - VkBuffer srcBuf = contents.buf; - - VkCommandBuffer cmd = GetNextCmd(); - - VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; - - vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - for(uint32_t i = 0; i < info.numUniqueMems; i++) - { - VkDeviceMemory dstMem = - GetResourceManager()->GetLiveHandle(info.memDataOffs[i].memory); - - ResourceId id = GetResID(dstMem); - - VkBuffer dstBuf = m_CreationInfo.m_Memory[id].wholeMemBuf; - - VkDeviceSize size = m_CreationInfo.m_Memory[id].wholeMemBufSize; - - // fill the whole memory from the given offset - VkBufferCopy region = {info.memDataOffs[i].memOffs, 0, size}; - - if(dstBuf != VK_NULL_HANDLE) - ObjDisp(cmd)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(dstBuf), 1, ®ion); - else - RDCERR("Whole memory buffer not present for %s", ToStr(id).c_str()); - } - - vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - FlushQ(); - - return true; -} - -bool WrappedVulkan::Apply_SparseInitialState(WrappedVkImage *im, const VkInitialContents &contents) -{ - const SparseImageInitState &info = contents.sparseImage; - - VkQueue q = GetQ(); - - if(info.opaque) - { - // unbind the entire image so that any new areas that are bound are unbound again - - // VKTODOLOW not sure if this is the right size for opaque portion of partial resident - // sparse image? how is that determined? - VkSparseMemoryBind unbind = {0, 0, VK_NULL_HANDLE, 0, 0}; - - VkMemoryRequirements mrq = {0}; - ObjDisp(q)->GetImageMemoryRequirements(Unwrap(GetDev()), im->real.As(), &mrq); - unbind.size = mrq.size; - - VkSparseImageOpaqueMemoryBindInfo opaqueBind = {im->real.As(), 1, &unbind}; - - VkSemaphore sem = GetNextSemaphore(); - - VkBindSparseInfo bindsparse = { - VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, - NULL, - 0, - NULL, // wait semaphores - 0, - NULL, // buffer bind - 1, - &opaqueBind, - 0, - NULL, // image bind - 1, - UnwrapPtr(sem), // signal semaphores - }; - - // first unbind all - ObjDisp(q)->QueueBindSparse(Unwrap(q), 1, &bindsparse, VK_NULL_HANDLE); - - // then make any bindings - if(info.opaqueCount > 0) - { - opaqueBind.bindCount = info.opaqueCount; - opaqueBind.pBinds = info.opaque; - - // wait for unbind semaphore - bindsparse.waitSemaphoreCount = 1; - bindsparse.pWaitSemaphores = bindsparse.pSignalSemaphores; - - bindsparse.signalSemaphoreCount = 0; - bindsparse.pSignalSemaphores = NULL; - - ObjDisp(q)->QueueBindSparse(Unwrap(q), 1, &bindsparse, VK_NULL_HANDLE); - } - - // marks that the above semaphore has been used, so next time we - // flush it will be moved back to the pool - SubmitSemaphores(); - } - - { - VkSparseImageMemoryBindInfo imgBinds[NUM_VK_IMAGE_ASPECTS]; - RDCEraseEl(imgBinds); - - VkBindSparseInfo bindsparse = { - VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, - NULL, - 0, - NULL, // wait semaphores - 0, - NULL, // buffer bind - 0, - NULL, // opaque bind - 0, - imgBinds, - 0, - NULL, // signal semaphores - }; - - // blat the page tables - for(uint32_t a = 0; a < NUM_VK_IMAGE_ASPECTS; a++) - { - if(!info.pageBinds[a]) - continue; - - imgBinds[bindsparse.imageBindCount].image = im->real.As(); - imgBinds[bindsparse.imageBindCount].bindCount = info.pageCount[a]; - imgBinds[bindsparse.imageBindCount].pBinds = info.pageBinds[a]; - - bindsparse.imageBindCount++; - } - - ObjDisp(q)->QueueBindSparse(Unwrap(q), 1, &bindsparse, VK_NULL_HANDLE); - } - - VkResult vkr = VK_SUCCESS; - - VkBuffer srcBuf = contents.buf; - - VkCommandBuffer cmd = GetNextCmd(); - - VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; - - vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - for(uint32_t i = 0; i < info.numUniqueMems; i++) - { - VkDeviceMemory dstMem = - GetResourceManager()->GetLiveHandle(info.memDataOffs[i].memory); - - ResourceId id = GetResID(dstMem); - - // since this is short lived it isn't wrapped. Note that we want - // to cache this up front, so it will then be wrapped - VkBuffer dstBuf = m_CreationInfo.m_Memory[id].wholeMemBuf; - VkDeviceSize size = m_CreationInfo.m_Memory[id].wholeMemBufSize; - - // fill the whole memory from the given offset - VkBufferCopy region = {info.memDataOffs[i].memOffs, 0, size}; - - if(dstBuf != VK_NULL_HANDLE) - ObjDisp(cmd)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(dstBuf), 1, ®ion); - else - RDCERR("Whole memory buffer not present for %s", ToStr(id).c_str()); - } - - vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); - RDCASSERTEQUAL(vkr, VK_SUCCESS); - - return true; -}