mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-09 17:17:11 +00:00
1b0fafc872
* For a few primary cases, we check to see if the API call failed (or is looking like it will fail, in the case of vulkan) and bail out. This will cause the capture to fail to load.
1554 lines
56 KiB
C++
1554 lines
56 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2015-2017 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"
|
|
|
|
// VKTODOLOW for depth-stencil images we are only save/restoring the depth, not the stencil
|
|
|
|
// VKTODOLOW there's a lot of duplicated code in this file for creating a buffer to do
|
|
// a memory copy and saving to disk.
|
|
// VKTODOLOW SerialiseComplexArray not having the ability to serialise into an in-memory
|
|
// array means some redundant copies.
|
|
// VKTODOLOW The code pattern for creating a few contiguous arrays all in one
|
|
// AllocAlignedBuffer for the initial contents buffer is ugly.
|
|
|
|
// VKTODOLOW in general we do a lot of "create buffer, use it, flush/sync then destroy".
|
|
// I don't know what the exact cost is, but it would be nice to batch up the buffers/etc
|
|
// used across init state use, and only do a single flush. Also we could then get some
|
|
// nice command buffer reuse (although need to be careful we don't create too large a
|
|
// command buffer that stalls the GPU).
|
|
// See INITSTATEBATCH
|
|
|
|
bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
|
|
{
|
|
ResourceId id = GetResourceManager()->GetID(res);
|
|
|
|
VkResourceType type = IdentifyTypeByPtr(res);
|
|
|
|
if(type == eResDescriptorSet)
|
|
{
|
|
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
|
|
RDCASSERT(record->descInfo && record->descInfo->layout);
|
|
const DescSetLayout &layout = *record->descInfo->layout;
|
|
|
|
uint32_t numElems = 0;
|
|
for(size_t i = 0; i < layout.bindings.size(); i++)
|
|
numElems += layout.bindings[i].descriptorCount;
|
|
|
|
DescriptorSetSlot *info =
|
|
(DescriptorSetSlot *)AllocAlignedBuffer(sizeof(DescriptorSetSlot) * numElems);
|
|
RDCEraseMem(info, sizeof(DescriptorSetSlot) * numElems);
|
|
|
|
uint32_t e = 0;
|
|
for(size_t i = 0; i < layout.bindings.size(); i++)
|
|
for(uint32_t b = 0; b < layout.bindings[i].descriptorCount; b++)
|
|
info[e++] = record->descInfo->descBindings[i][b];
|
|
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(type, NULL, 0, (byte *)info));
|
|
return true;
|
|
}
|
|
else if(type == eResBuffer)
|
|
{
|
|
WrappedVkBuffer *buffer = (WrappedVkBuffer *)res;
|
|
|
|
// buffers are only dirty if they are sparse
|
|
RDCASSERT(buffer->record->sparseInfo);
|
|
|
|
return Prepare_SparseInitialState(buffer);
|
|
}
|
|
else if(type == eResImage)
|
|
{
|
|
VkResult vkr = VK_SUCCESS;
|
|
|
|
WrappedVkImage *im = (WrappedVkImage *)res;
|
|
|
|
if(im->record->sparseInfo)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
VkDevice d = GetDev();
|
|
// INITSTATEBATCH
|
|
VkCommandBuffer cmd = GetNextCmd();
|
|
|
|
ImageLayouts *layout = NULL;
|
|
{
|
|
SCOPED_LOCK(m_ImageLayoutsLock);
|
|
layout = &m_ImageLayouts[im->id];
|
|
}
|
|
|
|
// must ensure offset remains valid. Must be multiple of block size, or 4, depending on format
|
|
VkDeviceSize bufAlignment = 4;
|
|
if(IsBlockFormat(layout->format))
|
|
bufAlignment = (VkDeviceSize)GetByteSize(1, 1, 1, layout->format, 0);
|
|
|
|
VkDeviceMemory readbackmem = VK_NULL_HANDLE;
|
|
|
|
VkBufferCreateInfo bufInfo = {
|
|
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
|
};
|
|
|
|
VkImage arrayIm = VK_NULL_HANDLE;
|
|
VkDeviceMemory arrayMem = VK_NULL_HANDLE;
|
|
|
|
VkImage realim = im->real.As<VkImage>();
|
|
int numLayers = layout->layerCount;
|
|
|
|
if(layout->sampleCount > 1)
|
|
{
|
|
// first decompose to array
|
|
numLayers *= layout->sampleCount;
|
|
|
|
VkImageCreateInfo arrayInfo = {
|
|
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
|
|
VK_IMAGE_TYPE_2D, layout->format, layout->extent, (uint32_t)layout->levelCount,
|
|
(uint32_t)numLayers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
|
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
|
VK_SHARING_MODE_EXCLUSIVE, 0, NULL, VK_IMAGE_LAYOUT_UNDEFINED,
|
|
};
|
|
|
|
if(IsDepthOrStencilFormat(layout->format))
|
|
arrayInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
else
|
|
arrayInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
|
|
vkr = ObjDisp(d)->CreateImage(Unwrap(d), &arrayInfo, NULL, &arrayIm);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkMemoryRequirements mrq = {0};
|
|
|
|
ObjDisp(d)->GetImageMemoryRequirements(Unwrap(d), arrayIm, &mrq);
|
|
|
|
VkMemoryAllocateInfo allocInfo = {
|
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
|
|
GetGPULocalMemoryIndex(mrq.memoryTypeBits),
|
|
};
|
|
|
|
vkr = ObjDisp(d)->AllocateMemory(Unwrap(d), &allocInfo, NULL, &arrayMem);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
vkr = ObjDisp(d)->BindImageMemory(Unwrap(d), arrayIm, arrayMem, 0);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
}
|
|
|
|
VkFormat sizeFormat = GetDepthOnlyFormat(layout->format);
|
|
|
|
for(int a = 0; a < numLayers; a++)
|
|
{
|
|
for(int m = 0; m < layout->levelCount; m++)
|
|
{
|
|
bufInfo.size = AlignUp(bufInfo.size, bufAlignment);
|
|
|
|
bufInfo.size += GetByteSize(layout->extent.width, layout->extent.height,
|
|
layout->extent.depth, sizeFormat, m);
|
|
|
|
if(sizeFormat != layout->format)
|
|
{
|
|
// if there's stencil and depth, allocate space for stencil
|
|
bufInfo.size = AlignUp(bufInfo.size, bufAlignment);
|
|
|
|
bufInfo.size += GetByteSize(layout->extent.width, layout->extent.height,
|
|
layout->extent.depth, VK_FORMAT_S8_UINT, m);
|
|
}
|
|
}
|
|
}
|
|
|
|
// since this is very short lived, it is not wrapped
|
|
VkBuffer dstBuf;
|
|
|
|
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &dstBuf);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkMemoryRequirements mrq = {0};
|
|
|
|
ObjDisp(d)->GetBufferMemoryRequirements(Unwrap(d), dstBuf, &mrq);
|
|
|
|
VkMemoryAllocateInfo allocInfo = {
|
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
|
|
GetReadbackMemoryIndex(mrq.memoryTypeBits),
|
|
};
|
|
|
|
vkr = ObjDisp(d)->AllocateMemory(Unwrap(d), &allocInfo, NULL, &readbackmem);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
GetResourceManager()->WrapResource(Unwrap(d), readbackmem);
|
|
|
|
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), dstBuf, Unwrap(readbackmem), 0);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
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);
|
|
|
|
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
if(IsStencilOnlyFormat(layout->format))
|
|
aspectFlags = VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
else if(IsDepthOrStencilFormat(layout->format))
|
|
aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
VkImageMemoryBarrier srcimBarrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
realim,
|
|
{aspectFlags, 0, (uint32_t)layout->levelCount, 0, (uint32_t)numLayers}};
|
|
|
|
if(aspectFlags == VK_IMAGE_ASPECT_DEPTH_BIT && !IsDepthOnlyFormat(layout->format))
|
|
srcimBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
// update the real image layout into transfer-source
|
|
srcimBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
if(arrayIm != VK_NULL_HANDLE)
|
|
srcimBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
// ensure all previous writes have completed
|
|
srcimBarrier.srcAccessMask = VK_ACCESS_ALL_WRITE_BITS;
|
|
// before we go reading
|
|
srcimBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
|
|
|
|
for(size_t si = 0; si < layout->subresourceStates.size(); si++)
|
|
{
|
|
srcimBarrier.subresourceRange = layout->subresourceStates[si].subresourceRange;
|
|
srcimBarrier.oldLayout = layout->subresourceStates[si].newLayout;
|
|
DoPipelineBarrier(cmd, 1, &srcimBarrier);
|
|
}
|
|
|
|
if(arrayIm != VK_NULL_HANDLE)
|
|
{
|
|
VkImageMemoryBarrier arrayimBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_GENERAL,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
arrayIm,
|
|
{srcimBarrier.subresourceRange.aspectMask, 0,
|
|
VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}};
|
|
|
|
DoPipelineBarrier(cmd, 1, &arrayimBarrier);
|
|
|
|
vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
GetDebugManager()->CopyTex2DMSToArray(arrayIm, realim, layout->extent, layout->layerCount,
|
|
layout->sampleCount, layout->format);
|
|
|
|
cmd = GetNextCmd();
|
|
|
|
vkr = ObjDisp(d)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
arrayimBarrier.srcAccessMask =
|
|
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
arrayimBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
arrayimBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
arrayimBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
|
|
DoPipelineBarrier(cmd, 1, &arrayimBarrier);
|
|
|
|
realim = arrayIm;
|
|
}
|
|
|
|
VkDeviceSize bufOffset = 0;
|
|
|
|
// loop over every slice/mip, copying it to the appropriate point in the buffer
|
|
for(int a = 0; a < numLayers; a++)
|
|
{
|
|
VkExtent3D extent = layout->extent;
|
|
|
|
for(int m = 0; m < layout->levelCount; m++)
|
|
{
|
|
VkBufferImageCopy region = {
|
|
0,
|
|
0,
|
|
0,
|
|
{aspectFlags, (uint32_t)m, (uint32_t)a, 1},
|
|
{
|
|
0, 0, 0,
|
|
},
|
|
extent,
|
|
};
|
|
|
|
bufOffset = AlignUp(bufOffset, bufAlignment);
|
|
|
|
region.bufferOffset = bufOffset;
|
|
|
|
bufOffset += GetByteSize(layout->extent.width, layout->extent.height, layout->extent.depth,
|
|
sizeFormat, m);
|
|
|
|
ObjDisp(d)->CmdCopyImageToBuffer(Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
dstBuf, 1, ®ion);
|
|
|
|
if(sizeFormat != layout->format)
|
|
{
|
|
// if we removed stencil from the format, copy that separately now.
|
|
bufOffset = AlignUp(bufOffset, bufAlignment);
|
|
|
|
region.bufferOffset = bufOffset;
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
bufOffset += GetByteSize(layout->extent.width, layout->extent.height,
|
|
layout->extent.depth, VK_FORMAT_S8_UINT, m);
|
|
|
|
ObjDisp(d)->CmdCopyImageToBuffer(
|
|
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuf, 1, ®ion);
|
|
}
|
|
|
|
// update the extent for the next mip
|
|
extent.width = RDCMAX(extent.width >> 1, 1U);
|
|
extent.height = RDCMAX(extent.height >> 1, 1U);
|
|
extent.depth = RDCMAX(extent.depth >> 1, 1U);
|
|
}
|
|
}
|
|
|
|
RDCASSERTMSG("buffer wasn't sized sufficiently!", bufOffset <= bufInfo.size, bufOffset,
|
|
mrq.size, layout->extent, layout->format, numLayers, layout->levelCount);
|
|
|
|
// transfer back to whatever it was
|
|
srcimBarrier.oldLayout = srcimBarrier.newLayout;
|
|
|
|
srcimBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
srcimBarrier.dstAccessMask = 0;
|
|
|
|
for(size_t si = 0; si < layout->subresourceStates.size(); si++)
|
|
{
|
|
srcimBarrier.subresourceRange = layout->subresourceStates[si].subresourceRange;
|
|
srcimBarrier.newLayout = layout->subresourceStates[si].newLayout;
|
|
srcimBarrier.dstAccessMask = MakeAccessMask(srcimBarrier.newLayout);
|
|
DoPipelineBarrier(cmd, 1, &srcimBarrier);
|
|
}
|
|
|
|
vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
// INITSTATEBATCH
|
|
SubmitCmds();
|
|
FlushQ();
|
|
|
|
ObjDisp(d)->DestroyBuffer(Unwrap(d), dstBuf, NULL);
|
|
|
|
if(arrayIm != VK_NULL_HANDLE)
|
|
{
|
|
ObjDisp(d)->DestroyImage(Unwrap(d), arrayIm, NULL);
|
|
ObjDisp(d)->FreeMemory(Unwrap(d), arrayMem, NULL);
|
|
}
|
|
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(type, GetWrapped(readbackmem),
|
|
(uint32_t)mrq.size, NULL));
|
|
|
|
return true;
|
|
}
|
|
else if(type == eResDeviceMemory)
|
|
{
|
|
VkResult vkr = VK_SUCCESS;
|
|
|
|
VkDevice d = GetDev();
|
|
// INITSTATEBATCH
|
|
VkCommandBuffer cmd = GetNextCmd();
|
|
|
|
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
|
|
VkDeviceSize dataoffs = 0;
|
|
VkDeviceMemory datamem = ToHandle<VkDeviceMemory>(res);
|
|
VkDeviceSize datasize = record->Length;
|
|
|
|
RDCASSERT(datamem != VK_NULL_HANDLE);
|
|
|
|
RDCASSERT(record->Length > 0);
|
|
VkDeviceSize memsize = record->Length;
|
|
|
|
VkDeviceMemory readbackmem = VK_NULL_HANDLE;
|
|
|
|
VkBufferCreateInfo bufInfo = {
|
|
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
|
};
|
|
|
|
// since these are very short lived, they are not wrapped
|
|
VkBuffer srcBuf, dstBuf;
|
|
|
|
// dstBuf is just over the allocated memory, so only the image's size
|
|
bufInfo.size = datasize;
|
|
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &dstBuf);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
// srcBuf spans the entire memory, then we copy out the sub-region we're interested in
|
|
bufInfo.size = memsize;
|
|
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &srcBuf);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkMemoryRequirements mrq = {0};
|
|
|
|
ObjDisp(d)->GetBufferMemoryRequirements(Unwrap(d), srcBuf, &mrq);
|
|
|
|
VkMemoryAllocateInfo allocInfo = {
|
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, datasize,
|
|
GetReadbackMemoryIndex(mrq.memoryTypeBits),
|
|
};
|
|
|
|
allocInfo.allocationSize = AlignUp(allocInfo.allocationSize, mrq.alignment);
|
|
|
|
vkr = ObjDisp(d)->AllocateMemory(Unwrap(d), &allocInfo, NULL, &readbackmem);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
GetResourceManager()->WrapResource(Unwrap(d), readbackmem);
|
|
|
|
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), srcBuf, datamem, 0);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), dstBuf, Unwrap(readbackmem), 0);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
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);
|
|
|
|
VkBufferCopy region = {dataoffs, 0, datasize};
|
|
|
|
ObjDisp(d)->CmdCopyBuffer(Unwrap(cmd), srcBuf, dstBuf, 1, ®ion);
|
|
|
|
vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
// INITSTATEBATCH
|
|
SubmitCmds();
|
|
FlushQ();
|
|
|
|
ObjDisp(d)->DestroyBuffer(Unwrap(d), srcBuf, NULL);
|
|
ObjDisp(d)->DestroyBuffer(Unwrap(d), dstBuf, NULL);
|
|
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(type, GetWrapped(readbackmem),
|
|
(uint32_t)datasize, NULL));
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unhandled resource type %d", type);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32_t WrappedVulkan::GetSize_InitialState(ResourceId id, WrappedVkRes *res)
|
|
{
|
|
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
|
|
VkResourceType type = IdentifyTypeByPtr(record->Resource);
|
|
VulkanResourceManager::InitialContentData initContents =
|
|
GetResourceManager()->GetInitialContents(id);
|
|
|
|
if(type == eResDescriptorSet)
|
|
{
|
|
RDCASSERT(record->descInfo && record->descInfo->layout);
|
|
const DescSetLayout &layout = *record->descInfo->layout;
|
|
|
|
uint32_t NumBindings = 0;
|
|
|
|
for(size_t i = 0; i < layout.bindings.size(); i++)
|
|
NumBindings += layout.bindings[i].descriptorCount;
|
|
|
|
return 32 + NumBindings * sizeof(DescriptorSetSlot);
|
|
}
|
|
else if(type == eResBuffer)
|
|
{
|
|
// buffers only have initial states when they're sparse
|
|
return GetSize_SparseInitialState(id, res);
|
|
}
|
|
else if(type == eResImage || type == eResDeviceMemory)
|
|
{
|
|
bool IsSparse = (initContents.blob != NULL);
|
|
|
|
if(IsSparse)
|
|
return GetSize_SparseInitialState(id, res);
|
|
|
|
// the size primarily comes from the buffer, the size of which we conveniently have stored.
|
|
return uint32_t(128 + initContents.num + WriteSerialiser::GetChunkAlignment());
|
|
}
|
|
|
|
RDCERR("Unhandled resource type %s", ToStr(type).c_str());
|
|
return 128;
|
|
}
|
|
|
|
// second parameter isn't used, as we might be serialising init state for a deleted resource
|
|
template <typename SerialiserType>
|
|
bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, WrappedVkRes *)
|
|
{
|
|
VkResourceType type;
|
|
|
|
VkResourceRecord *record = NULL;
|
|
if(ser.IsWriting())
|
|
{
|
|
record = GetResourceManager()->GetResourceRecord(id);
|
|
// use the record's resource, not the one passed in, because the passed in one
|
|
// might be null if it was deleted
|
|
type = IdentifyTypeByPtr(record->Resource);
|
|
}
|
|
|
|
bool ret = true;
|
|
|
|
SERIALISE_ELEMENT(type);
|
|
SERIALISE_ELEMENT(id);
|
|
|
|
if(IsReplayingAndReading())
|
|
{
|
|
AddResourceCurChunk(id);
|
|
}
|
|
|
|
if(type == eResDescriptorSet)
|
|
{
|
|
DescriptorSetSlot *Bindings = NULL;
|
|
uint32_t NumBindings = 0;
|
|
|
|
// while writing, fetching binding information from prepared initial contents
|
|
if(ser.IsWriting())
|
|
{
|
|
VulkanResourceManager::InitialContentData initContents =
|
|
GetResourceManager()->GetInitialContents(id);
|
|
|
|
RDCASSERT(record->descInfo && record->descInfo->layout);
|
|
const DescSetLayout &layout = *record->descInfo->layout;
|
|
|
|
Bindings = (DescriptorSetSlot *)initContents.blob;
|
|
|
|
for(size_t i = 0; i < layout.bindings.size(); i++)
|
|
NumBindings += layout.bindings[i].descriptorCount;
|
|
}
|
|
|
|
SERIALISE_ELEMENT_ARRAY(Bindings, NumBindings);
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
// while reading, fetch the binding information and allocate a VkWriteDescriptorSet array
|
|
if(IsReplayingAndReading())
|
|
{
|
|
WrappedVkRes *res = GetResourceManager()->GetLiveResource(id);
|
|
ResourceId liveid = GetResourceManager()->GetLiveID(id);
|
|
|
|
const DescSetLayout &layout =
|
|
m_CreationInfo.m_DescSetLayout[m_DescriptorSetState[liveid].layout];
|
|
|
|
uint32_t numBinds = (uint32_t)layout.bindings.size();
|
|
|
|
// allocate memory to keep the element structures around, as well as a WriteDescriptorSet
|
|
// array
|
|
byte *blob = AllocAlignedBuffer(sizeof(VkDescriptorBufferInfo) * NumBindings +
|
|
sizeof(VkWriteDescriptorSet) * numBinds);
|
|
|
|
RDCCOMPILE_ASSERT(sizeof(VkDescriptorBufferInfo) >= sizeof(VkDescriptorImageInfo),
|
|
"Descriptor structs sizes are unexpected, ensure largest size is used");
|
|
|
|
VkWriteDescriptorSet *writes = (VkWriteDescriptorSet *)blob;
|
|
VkDescriptorBufferInfo *dstData = (VkDescriptorBufferInfo *)(writes + numBinds);
|
|
DescriptorSetSlot *srcData = Bindings;
|
|
|
|
uint32_t validBinds = numBinds;
|
|
|
|
// i is the writedescriptor that we're updating, could be
|
|
// lower than j if a writedescriptor ended up being no-op and
|
|
// was skipped. j is the actual index.
|
|
for(uint32_t i = 0, j = 0; j < numBinds; j++)
|
|
{
|
|
writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
writes[i].pNext = NULL;
|
|
|
|
// update whole element (array or single)
|
|
writes[i].dstSet = (VkDescriptorSet)(uint64_t)res;
|
|
writes[i].dstBinding = j;
|
|
writes[i].dstArrayElement = 0;
|
|
writes[i].descriptorCount = layout.bindings[j].descriptorCount;
|
|
writes[i].descriptorType = layout.bindings[j].descriptorType;
|
|
|
|
DescriptorSetSlot *src = srcData;
|
|
srcData += layout.bindings[j].descriptorCount;
|
|
|
|
// will be cast to the appropriate type, we just need to increment
|
|
// the dstData pointer by worst case size
|
|
VkDescriptorBufferInfo *dstBuffer = dstData;
|
|
VkDescriptorImageInfo *dstImage = (VkDescriptorImageInfo *)dstData;
|
|
VkBufferView *dstTexelBuffer = (VkBufferView *)dstData;
|
|
dstData += layout.bindings[j].descriptorCount;
|
|
|
|
// the correct one will be set below
|
|
writes[i].pBufferInfo = NULL;
|
|
writes[i].pImageInfo = NULL;
|
|
writes[i].pTexelBufferView = NULL;
|
|
|
|
// check that the resources we need for this write are present,
|
|
// as some might have been skipped due to stale descriptor set
|
|
// slots or otherwise unreferenced objects (the descriptor set
|
|
// initial contents do not cause a frame reference for their
|
|
// resources
|
|
//
|
|
// While we go, we copy from the DescriptorSetSlot structures to
|
|
// the appropriate array in the VkWriteDescriptorSet for the
|
|
// descriptor type
|
|
bool valid = true;
|
|
|
|
// quick check for slots that were completely uninitialised
|
|
// and so don't have valid data
|
|
if(src->texelBufferView == VK_NULL_HANDLE && src->imageInfo.sampler == VK_NULL_HANDLE &&
|
|
src->imageInfo.imageView == VK_NULL_HANDLE && src->bufferInfo.buffer == VK_NULL_HANDLE)
|
|
{
|
|
valid = false;
|
|
}
|
|
else
|
|
{
|
|
switch(writes[i].descriptorType)
|
|
{
|
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
{
|
|
for(uint32_t d = 0; d < writes[i].descriptorCount; d++)
|
|
{
|
|
dstImage[d] = src[d].imageInfo;
|
|
valid &= (src[d].imageInfo.sampler != VK_NULL_HANDLE);
|
|
}
|
|
writes[i].pImageInfo = dstImage;
|
|
break;
|
|
}
|
|
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
|
{
|
|
for(uint32_t d = 0; d < writes[i].descriptorCount; d++)
|
|
{
|
|
dstImage[d] = src[d].imageInfo;
|
|
valid &= (src[d].imageInfo.sampler != VK_NULL_HANDLE) ||
|
|
(layout.bindings[j].immutableSampler &&
|
|
layout.bindings[j].immutableSampler[d] != ResourceId());
|
|
valid &= (src[d].imageInfo.imageView != VK_NULL_HANDLE);
|
|
}
|
|
writes[i].pImageInfo = dstImage;
|
|
break;
|
|
}
|
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
|
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
|
{
|
|
for(uint32_t d = 0; d < writes[i].descriptorCount; d++)
|
|
{
|
|
dstImage[d] = src[d].imageInfo;
|
|
valid &= (src[d].imageInfo.imageView != VK_NULL_HANDLE);
|
|
}
|
|
writes[i].pImageInfo = dstImage;
|
|
break;
|
|
}
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
|
{
|
|
for(uint32_t d = 0; d < writes[i].descriptorCount; d++)
|
|
{
|
|
dstTexelBuffer[d] = src[d].texelBufferView;
|
|
valid &= (src[d].texelBufferView != VK_NULL_HANDLE);
|
|
}
|
|
writes[i].pTexelBufferView = dstTexelBuffer;
|
|
break;
|
|
}
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
|
{
|
|
for(uint32_t d = 0; d < writes[i].descriptorCount; d++)
|
|
{
|
|
dstBuffer[d] = src[d].bufferInfo;
|
|
valid &= (src[d].bufferInfo.buffer != VK_NULL_HANDLE);
|
|
}
|
|
writes[i].pBufferInfo = dstBuffer;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
RDCERR("Unexpected descriptor type %d", writes[i].descriptorType);
|
|
ret = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if this write is not valid, skip it
|
|
// and start writing the next one in here
|
|
if(!valid)
|
|
validBinds--;
|
|
else
|
|
i++;
|
|
}
|
|
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(type, NULL, validBinds, blob));
|
|
}
|
|
}
|
|
else if(type == eResBuffer)
|
|
{
|
|
// buffers only have initial states when they're sparse
|
|
return Serialise_SparseBufferInitialState(ser, id, GetResourceManager()->GetInitialContents(id));
|
|
}
|
|
else if(type == eResDeviceMemory || type == eResImage)
|
|
{
|
|
VkDevice d = !IsStructuredExporting(m_State) ? GetDev() : VK_NULL_HANDLE;
|
|
VulkanResourceManager::InitialContentData initContents =
|
|
GetResourceManager()->GetInitialContents(id);
|
|
|
|
// 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, bool(initContents.blob != NULL));
|
|
|
|
if(IsSparse)
|
|
{
|
|
ret = false;
|
|
|
|
if(type == eResImage)
|
|
{
|
|
ret = Serialise_SparseImageInitialState(ser, id, initContents);
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Invalid initial state - sparse marker for device memory");
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
VkResult vkr = VK_SUCCESS;
|
|
|
|
byte *Contents = NULL;
|
|
uint64_t ContentsSize = (uint64_t)initContents.num;
|
|
VkDeviceMemory mappedMem = VK_NULL_HANDLE;
|
|
|
|
// 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.
|
|
VkDeviceMemory uploadMemory = VK_NULL_HANDLE;
|
|
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 = (VkDeviceMemory)(uint64_t)initContents.resource;
|
|
vkr = ObjDisp(d)->MapMemory(Unwrap(d), Unwrap(mappedMem), 0, VK_WHOLE_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);
|
|
|
|
VkMemoryRequirements mrq = {0};
|
|
|
|
ObjDisp(d)->GetBufferMemoryRequirements(Unwrap(d), Unwrap(uploadBuf), &mrq);
|
|
|
|
VkMemoryAllocateInfo allocInfo = {
|
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
|
|
GetUploadMemoryIndex(mrq.memoryTypeBits),
|
|
};
|
|
|
|
vkr = ObjDisp(d)->AllocateMemory(Unwrap(d), &allocInfo, NULL, &uploadMemory);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
GetResourceManager()->WrapResource(Unwrap(d), uploadMemory);
|
|
|
|
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(uploadBuf), Unwrap(uploadMemory), 0);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
mappedMem = uploadMemory;
|
|
|
|
ObjDisp(d)->MapMemory(Unwrap(d), Unwrap(uploadMemory), 0, VK_WHOLE_SIZE, 0, (void **)&Contents);
|
|
}
|
|
|
|
// not using SERIALISE_ELEMENT_ARRAY so we can deliberately avoid allocation - we serialise
|
|
// directly into upload memory
|
|
ser.Serialise("Contents", 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)
|
|
ObjDisp(d)->UnmapMemory(Unwrap(d), Unwrap(mappedMem));
|
|
|
|
SERIALISE_CHECK_READ_ERRORS();
|
|
|
|
// if we're handling a device memory object, we're done - we note the memory object to delete at
|
|
// the end of the program, and store the buffer to copy off in Apply
|
|
if(IsReplayingAndReading())
|
|
{
|
|
ResourceId liveid = GetResourceManager()->GetLiveID(id);
|
|
|
|
if(type == eResDeviceMemory)
|
|
{
|
|
m_CleanupMems.push_back(uploadMemory);
|
|
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(type, GetWrapped(uploadBuf),
|
|
(uint32_t)ContentsSize, NULL));
|
|
}
|
|
else
|
|
{
|
|
VulkanResourceManager::InitialContentData initial(type, GetWrapped(uploadBuf), 0, NULL);
|
|
|
|
VulkanCreationInfo::Image &c = m_CreationInfo.m_Image[liveid];
|
|
|
|
// for non-MSAA images, we're done - we'll do buffer-to-image copies with appropriate
|
|
// offsets
|
|
// to copy out the subresources into the image itself.
|
|
if(c.samples == VK_SAMPLE_COUNT_1_BIT)
|
|
{
|
|
m_CleanupMems.push_back(uploadMemory);
|
|
}
|
|
else
|
|
{
|
|
// MSAA textures we upload into an array image, then the apply does an array-to-MSAA copy
|
|
// instead of the usual buffer-to-image copies.
|
|
int numLayers = c.arrayLayers * (int)c.samples;
|
|
|
|
VkImageCreateInfo arrayInfo = {
|
|
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
NULL,
|
|
VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
|
|
VK_IMAGE_TYPE_2D,
|
|
c.format,
|
|
c.extent,
|
|
(uint32_t)c.mipLevels,
|
|
(uint32_t)numLayers,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
|
VK_SHARING_MODE_EXCLUSIVE,
|
|
0,
|
|
NULL,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
};
|
|
|
|
VkImage arrayIm;
|
|
|
|
vkr = ObjDisp(d)->CreateImage(Unwrap(d), &arrayInfo, NULL, &arrayIm);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
GetResourceManager()->WrapResource(Unwrap(d), arrayIm);
|
|
|
|
VkMemoryRequirements mrq = {0};
|
|
ObjDisp(d)->GetImageMemoryRequirements(Unwrap(d), Unwrap(arrayIm), &mrq);
|
|
|
|
VkMemoryAllocateInfo allocInfo = {
|
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
|
|
GetGPULocalMemoryIndex(mrq.memoryTypeBits),
|
|
};
|
|
|
|
VkDeviceMemory arrayMem;
|
|
|
|
vkr = ObjDisp(d)->AllocateMemory(Unwrap(d), &allocInfo, NULL, &arrayMem);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
GetResourceManager()->WrapResource(Unwrap(d), arrayMem);
|
|
|
|
vkr = ObjDisp(d)->BindImageMemory(Unwrap(d), Unwrap(arrayIm), Unwrap(arrayMem), 0);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
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);
|
|
|
|
VkExtent3D extent = c.extent;
|
|
|
|
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
VkFormat fmt = c.format;
|
|
if(IsStencilOnlyFormat(fmt))
|
|
aspectFlags = VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
else if(IsDepthOrStencilFormat(fmt))
|
|
aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
VkImageMemoryBarrier dstimBarrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
Unwrap(arrayIm),
|
|
{aspectFlags, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}};
|
|
|
|
if(aspectFlags == VK_IMAGE_ASPECT_DEPTH_BIT && !IsDepthOnlyFormat(fmt))
|
|
dstimBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
DoPipelineBarrier(cmd, 1, &dstimBarrier);
|
|
|
|
VkDeviceSize bufOffset = 0;
|
|
|
|
// must ensure offset remains valid. Must be multiple of block size, or 4, depending on
|
|
// format
|
|
VkDeviceSize bufAlignment = 4;
|
|
if(IsBlockFormat(fmt))
|
|
bufAlignment = (VkDeviceSize)GetByteSize(1, 1, 1, fmt, 0);
|
|
|
|
std::vector<VkBufferImageCopy> mainCopies, stencilCopies;
|
|
|
|
// copy each slice/mip individually
|
|
for(int a = 0; a < numLayers; a++)
|
|
{
|
|
extent = c.extent;
|
|
|
|
for(int m = 0; m < c.mipLevels; m++)
|
|
{
|
|
VkBufferImageCopy region = {
|
|
0,
|
|
0,
|
|
0,
|
|
{aspectFlags, (uint32_t)m, (uint32_t)a, 1},
|
|
{
|
|
0, 0, 0,
|
|
},
|
|
extent,
|
|
};
|
|
|
|
bufOffset = AlignUp(bufOffset, bufAlignment);
|
|
|
|
region.bufferOffset = bufOffset;
|
|
|
|
VkFormat sizeFormat = GetDepthOnlyFormat(fmt);
|
|
|
|
// pass 0 for mip since we've already pre-downscaled extent
|
|
bufOffset += GetByteSize(extent.width, extent.height, extent.depth, sizeFormat, 0);
|
|
|
|
mainCopies.push_back(region);
|
|
|
|
if(sizeFormat != fmt)
|
|
{
|
|
// if we removed stencil from the format, copy that separately now.
|
|
bufOffset = AlignUp(bufOffset, bufAlignment);
|
|
|
|
region.bufferOffset = bufOffset;
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
bufOffset +=
|
|
GetByteSize(extent.width, extent.height, extent.depth, VK_FORMAT_S8_UINT, 0);
|
|
|
|
stencilCopies.push_back(region);
|
|
}
|
|
|
|
// update the extent for the next mip
|
|
extent.width = RDCMAX(extent.width >> 1, 1U);
|
|
extent.height = RDCMAX(extent.height >> 1, 1U);
|
|
extent.depth = RDCMAX(extent.depth >> 1, 1U);
|
|
}
|
|
}
|
|
|
|
ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), Unwrap(uploadBuf), Unwrap(arrayIm),
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
(uint32_t)mainCopies.size(), &mainCopies[0]);
|
|
|
|
if(!stencilCopies.empty())
|
|
ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), Unwrap(uploadBuf), Unwrap(arrayIm),
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
(uint32_t)stencilCopies.size(), &stencilCopies[0]);
|
|
|
|
// once transfers complete, get ready for copy array->ms
|
|
dstimBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
dstimBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
dstimBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
dstimBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
|
|
DoPipelineBarrier(cmd, 1, &dstimBarrier);
|
|
|
|
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
// INITSTATEBATCH
|
|
SubmitCmds();
|
|
FlushQ();
|
|
|
|
// destroy the buffer and de-allocate as it's no longer needed
|
|
vkDestroyBuffer(d, uploadBuf, NULL);
|
|
vkFreeMemory(d, uploadMemory, NULL);
|
|
|
|
// the array-backing memory must be cleaned-up later as we'll be using the array image to
|
|
// apply from.
|
|
m_CleanupMems.push_back(arrayMem);
|
|
initial.resource = GetWrapped(arrayIm);
|
|
}
|
|
|
|
GetResourceManager()->SetInitialContents(id, initial);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unhandled resource type %s", ToStr(type).c_str());
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
template bool WrappedVulkan::Serialise_InitialState(ReadSerialiser &ser, ResourceId resid,
|
|
WrappedVkRes *);
|
|
template bool WrappedVulkan::Serialise_InitialState(WriteSerialiser &ser, ResourceId resid,
|
|
WrappedVkRes *);
|
|
|
|
void WrappedVulkan::Create_InitialState(ResourceId id, WrappedVkRes *live, bool hasData)
|
|
{
|
|
if(IsStructuredExporting(m_State))
|
|
return;
|
|
|
|
VkResourceType type = IdentifyTypeByPtr(live);
|
|
|
|
if(type == eResDescriptorSet)
|
|
{
|
|
// There is no sensible default for a descriptor set to create. The contents are
|
|
// undefined until written to. This means if a descriptor set was alloc'd within a
|
|
// frame (the only time we won't have initial contents tracked for it) then the
|
|
// contents are undefined, so using whatever is currently in the set is fine. Reading
|
|
// from it (and thus getting data from later in the frame potentially) is an error.
|
|
//
|
|
// Note the same kind of problem applies if a descriptor set is alloc'd before the
|
|
// frame and then say slot 5 is never written to until the middle of the frame, then
|
|
// used. The initial states we have prepared won't have anything valid for 5 so when
|
|
// we apply we won't even write anything into slot 5 - the same case as if we had
|
|
// no initial states at all for that descriptor set
|
|
}
|
|
else if(type == eResImage)
|
|
{
|
|
ResourceId liveid = GetResourceManager()->GetLiveID(id);
|
|
|
|
if(m_ImageLayouts.find(liveid) == m_ImageLayouts.end())
|
|
{
|
|
RDCERR("Couldn't find image info for %llu", id);
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(type, NULL,
|
|
eInitialContents_ClearColorImage, NULL));
|
|
return;
|
|
}
|
|
|
|
ImageLayouts &layouts = m_ImageLayouts[liveid];
|
|
|
|
if(layouts.subresourceStates[0].subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(type, NULL,
|
|
eInitialContents_ClearColorImage, NULL));
|
|
else
|
|
GetResourceManager()->SetInitialContents(
|
|
id, VulkanResourceManager::InitialContentData(
|
|
type, NULL, eInitialContents_ClearDepthStencilImage, NULL));
|
|
}
|
|
else if(type == eResDeviceMemory)
|
|
{
|
|
// ignore, it was probably dirty but not referenced in the frame
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unhandled resource type %d", type);
|
|
}
|
|
}
|
|
|
|
void WrappedVulkan::Apply_InitialState(WrappedVkRes *live,
|
|
VulkanResourceManager::InitialContentData initial)
|
|
{
|
|
VkResourceType type = (VkResourceType)initial.resourceType;
|
|
|
|
ResourceId id = GetResourceManager()->GetID(live);
|
|
|
|
if(type == eResDescriptorSet)
|
|
{
|
|
VkWriteDescriptorSet *writes = (VkWriteDescriptorSet *)initial.blob;
|
|
|
|
// if it ended up that no descriptors were valid, just skip
|
|
if(initial.num == 0)
|
|
return;
|
|
|
|
// deliberately go through our wrapper implementation, to unwrap the VkWriteDescriptorSet
|
|
// structs
|
|
vkUpdateDescriptorSets(GetDev(), initial.num, writes, 0, NULL);
|
|
|
|
// need to blat over the current descriptor set contents, so these are available
|
|
// when we want to fetch pipeline state
|
|
vector<DescriptorSetSlot *> &bindings = m_DescriptorSetState[id].currentBindings;
|
|
|
|
for(uint32_t i = 0; i < initial.num; i++)
|
|
{
|
|
RDCASSERT(writes[i].dstBinding < bindings.size());
|
|
RDCASSERT(writes[i].dstArrayElement == 0);
|
|
|
|
DescriptorSetSlot *bind = bindings[writes[i].dstBinding];
|
|
|
|
for(uint32_t d = 0; d < writes[i].descriptorCount; d++)
|
|
{
|
|
if(writes[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ||
|
|
writes[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
|
|
{
|
|
bind[d].texelBufferView = writes[i].pTexelBufferView[d];
|
|
}
|
|
else if(writes[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
|
|
writes[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
|
|
writes[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
|
|
writes[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
|
|
{
|
|
bind[d].bufferInfo = writes[i].pBufferInfo[d];
|
|
}
|
|
else
|
|
{
|
|
bind[d].imageInfo = writes[i].pImageInfo[d];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(type == eResBuffer)
|
|
{
|
|
Apply_SparseInitialState((WrappedVkBuffer *)live, initial);
|
|
}
|
|
else if(type == eResImage)
|
|
{
|
|
VkResult vkr = VK_SUCCESS;
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
|
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
|
|
|
|
if(initial.blob != NULL)
|
|
{
|
|
RDCASSERT(initial.num == eInitialContents_Sparse);
|
|
Apply_SparseInitialState((WrappedVkImage *)live, initial);
|
|
return;
|
|
}
|
|
|
|
// handle any 'created' initial states, without an actual image with contents
|
|
if(initial.resource == NULL)
|
|
{
|
|
RDCASSERT(initial.num != eInitialContents_Sparse);
|
|
if(initial.num == eInitialContents_ClearColorImage)
|
|
{
|
|
if(IsBlockFormat(m_ImageLayouts[id].format))
|
|
{
|
|
RDCWARN(
|
|
"Trying to clear a compressed image %llu - should have initial states or be "
|
|
"stripped.",
|
|
id);
|
|
return;
|
|
}
|
|
|
|
VkCommandBuffer cmd = GetNextCmd();
|
|
|
|
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkImageMemoryBarrier barrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
ToHandle<VkImage>(live),
|
|
{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS},
|
|
};
|
|
|
|
// finish any pending work before clear
|
|
barrier.srcAccessMask = VK_ACCESS_ALL_WRITE_BITS;
|
|
// clear completes before subsequent operations
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
barrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
barrier.oldLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
DoPipelineBarrier(cmd, 1, &barrier);
|
|
}
|
|
|
|
VkClearColorValue clearval = {};
|
|
VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
|
|
VK_REMAINING_ARRAY_LAYERS};
|
|
|
|
ObjDisp(cmd)->CmdClearColorImage(Unwrap(cmd), ToHandle<VkImage>(live),
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearval, 1, &range);
|
|
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
|
|
// complete clear before any other work
|
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
barrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
barrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
barrier.newLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
barrier.dstAccessMask |= MakeAccessMask(barrier.newLayout);
|
|
DoPipelineBarrier(cmd, 1, &barrier);
|
|
}
|
|
|
|
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
#if ENABLED(SINGLE_FLUSH_VALIDATE)
|
|
SubmitCmds();
|
|
#endif
|
|
}
|
|
else if(initial.num == eInitialContents_ClearDepthStencilImage)
|
|
{
|
|
VkCommandBuffer cmd = GetNextCmd();
|
|
|
|
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkImageMemoryBarrier barrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
ToHandle<VkImage>(live),
|
|
{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS},
|
|
};
|
|
|
|
// finish any pending work before clear
|
|
barrier.srcAccessMask = VK_ACCESS_ALL_WRITE_BITS;
|
|
// clear completes before subsequent operations
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
barrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
barrier.oldLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
DoPipelineBarrier(cmd, 1, &barrier);
|
|
}
|
|
|
|
VkClearDepthStencilValue clearval = {1.0f, 0};
|
|
VkImageSubresourceRange range = {barrier.subresourceRange.aspectMask, 0,
|
|
VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS};
|
|
|
|
ObjDisp(cmd)->CmdClearDepthStencilImage(Unwrap(cmd), ToHandle<VkImage>(live),
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearval, 1,
|
|
&range);
|
|
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
|
|
// complete clear before any other work
|
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
barrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
barrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
barrier.newLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
DoPipelineBarrier(cmd, 1, &barrier);
|
|
}
|
|
|
|
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
#if ENABLED(SINGLE_FLUSH_VALIDATE)
|
|
SubmitCmds();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unexpected initial state type %u with NULL resource", initial.num);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if(m_CreationInfo.m_Image[id].samples != VK_SAMPLE_COUNT_1_BIT)
|
|
{
|
|
VkCommandBuffer cmd = GetNextCmd();
|
|
|
|
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
VulkanCreationInfo::Image &c = m_CreationInfo.m_Image[id];
|
|
|
|
VkFormat fmt = c.format;
|
|
if(IsStencilOnlyFormat(fmt))
|
|
aspectFlags = VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
else if(IsDepthOrStencilFormat(fmt))
|
|
aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
if(aspectFlags == VK_IMAGE_ASPECT_DEPTH_BIT && !IsDepthOnlyFormat(fmt))
|
|
aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
VkImageMemoryBarrier barrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_GENERAL,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
ToHandle<VkImage>(live),
|
|
{aspectFlags, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS},
|
|
};
|
|
|
|
barrier.srcAccessMask = VK_ACCESS_ALL_WRITE_BITS;
|
|
barrier.dstAccessMask =
|
|
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
barrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
barrier.oldLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
DoPipelineBarrier(cmd, 1, &barrier);
|
|
}
|
|
|
|
WrappedVkImage *arrayIm = (WrappedVkImage *)initial.resource;
|
|
|
|
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
GetDebugManager()->CopyArrayToTex2DMS(ToHandle<VkImage>(live), arrayIm->real.As<VkImage>(),
|
|
c.extent, (uint32_t)c.arrayLayers, (uint32_t)c.samples,
|
|
fmt);
|
|
|
|
cmd = GetNextCmd();
|
|
|
|
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
// complete copy before any other work
|
|
barrier.srcAccessMask =
|
|
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
barrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
barrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
barrier.newLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
barrier.dstAccessMask |= MakeAccessMask(barrier.newLayout);
|
|
DoPipelineBarrier(cmd, 1, &barrier);
|
|
}
|
|
|
|
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
#if ENABLED(SINGLE_FLUSH_VALIDATE)
|
|
SubmitCmds();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
WrappedVkBuffer *buf = (WrappedVkBuffer *)initial.resource;
|
|
|
|
VkCommandBuffer cmd = GetNextCmd();
|
|
|
|
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkExtent3D extent = m_CreationInfo.m_Image[id].extent;
|
|
|
|
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
VkFormat fmt = m_CreationInfo.m_Image[id].format;
|
|
if(IsStencilOnlyFormat(fmt))
|
|
aspectFlags = VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
else if(IsDepthOrStencilFormat(fmt))
|
|
aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
VkImageMemoryBarrier dstimBarrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
NULL,
|
|
0,
|
|
0,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
VK_QUEUE_FAMILY_IGNORED,
|
|
ToHandle<VkImage>(live),
|
|
{aspectFlags, 0, 1, 0, (uint32_t)m_CreationInfo.m_Image[id].arrayLayers}};
|
|
|
|
if(aspectFlags == VK_IMAGE_ASPECT_DEPTH_BIT && !IsDepthOnlyFormat(fmt))
|
|
dstimBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
VkDeviceSize bufOffset = 0;
|
|
|
|
// must ensure offset remains valid. Must be multiple of block size, or 4, depending on format
|
|
VkDeviceSize bufAlignment = 4;
|
|
if(IsBlockFormat(fmt))
|
|
bufAlignment = (VkDeviceSize)GetByteSize(1, 1, 1, fmt, 0);
|
|
|
|
// first update the live image layout into destination optimal (the initial state
|
|
// image is always and permanently in source optimal already).
|
|
dstimBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
dstimBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
dstimBarrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
dstimBarrier.oldLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
dstimBarrier.srcAccessMask = VK_ACCESS_ALL_WRITE_BITS | MakeAccessMask(dstimBarrier.oldLayout);
|
|
DoPipelineBarrier(cmd, 1, &dstimBarrier);
|
|
}
|
|
|
|
// copy each slice/mip individually
|
|
for(int a = 0; a < m_CreationInfo.m_Image[id].arrayLayers; a++)
|
|
{
|
|
extent = m_CreationInfo.m_Image[id].extent;
|
|
|
|
for(int m = 0; m < m_CreationInfo.m_Image[id].mipLevels; m++)
|
|
{
|
|
VkBufferImageCopy region = {
|
|
0,
|
|
0,
|
|
0,
|
|
{aspectFlags, (uint32_t)m, (uint32_t)a, 1},
|
|
{
|
|
0, 0, 0,
|
|
},
|
|
extent,
|
|
};
|
|
|
|
bufOffset = AlignUp(bufOffset, bufAlignment);
|
|
|
|
region.bufferOffset = bufOffset;
|
|
|
|
VkFormat sizeFormat = GetDepthOnlyFormat(fmt);
|
|
|
|
// pass 0 for mip since we've already pre-downscaled extent
|
|
bufOffset += GetByteSize(extent.width, extent.height, extent.depth, sizeFormat, 0);
|
|
|
|
ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), buf->real.As<VkBuffer>(),
|
|
ToHandle<VkImage>(live),
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
|
|
|
if(sizeFormat != fmt)
|
|
{
|
|
// if we removed stencil from the format, copy that separately now.
|
|
bufOffset = AlignUp(bufOffset, bufAlignment);
|
|
|
|
region.bufferOffset = bufOffset;
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
bufOffset += GetByteSize(extent.width, extent.height, extent.depth, VK_FORMAT_S8_UINT, 0);
|
|
|
|
ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), buf->real.As<VkBuffer>(),
|
|
ToHandle<VkImage>(live),
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
|
}
|
|
|
|
// update the extent for the next mip
|
|
extent.width = RDCMAX(extent.width >> 1, 1U);
|
|
extent.height = RDCMAX(extent.height >> 1, 1U);
|
|
extent.depth = RDCMAX(extent.depth >> 1, 1U);
|
|
}
|
|
}
|
|
|
|
// update the live image layout back
|
|
dstimBarrier.oldLayout = dstimBarrier.newLayout;
|
|
|
|
// make sure the apply completes before any further work
|
|
dstimBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
dstimBarrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS;
|
|
|
|
for(size_t si = 0; si < m_ImageLayouts[id].subresourceStates.size(); si++)
|
|
{
|
|
dstimBarrier.subresourceRange = m_ImageLayouts[id].subresourceStates[si].subresourceRange;
|
|
dstimBarrier.newLayout = m_ImageLayouts[id].subresourceStates[si].newLayout;
|
|
dstimBarrier.dstAccessMask |= MakeAccessMask(dstimBarrier.newLayout);
|
|
DoPipelineBarrier(cmd, 1, &dstimBarrier);
|
|
}
|
|
|
|
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
#if ENABLED(SINGLE_FLUSH_VALIDATE)
|
|
SubmitCmds();
|
|
#endif
|
|
}
|
|
else if(type == eResDeviceMemory)
|
|
{
|
|
VkResult vkr = VK_SUCCESS;
|
|
|
|
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
|
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
|
|
|
|
VkBuffer srcBuf = (VkBuffer)(uint64_t)initial.resource;
|
|
VkDeviceSize datasize = (VkDeviceSize)initial.num;
|
|
VkDeviceSize dstMemOffs = 0;
|
|
|
|
VkCommandBuffer cmd = GetNextCmd();
|
|
|
|
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
VkBuffer dstBuf = m_CreationInfo.m_Memory[id].wholeMemBuf;
|
|
|
|
VkBufferCopy region = {0, dstMemOffs, datasize};
|
|
|
|
ObjDisp(cmd)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(dstBuf), 1, ®ion);
|
|
|
|
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
|
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
|
|
|
#if ENABLED(SINGLE_FLUSH_VALIDATE)
|
|
SubmitCmds();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unhandled resource type %d", type);
|
|
}
|
|
}
|