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;
-}