Files
renderdoc/renderdoc/driver/vulkan/vk_initstate.cpp
T
2016-02-07 18:44:07 +01:00

521 lines
17 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015 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"
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->layout);
const DescSetLayout &layout = *record->layout;
uint32_t numElems = 0;
for(size_t i=0; i < layout.bindings.size(); i++)
numElems += layout.bindings[i].arraySize;
VkDescriptorInfo *info = (VkDescriptorInfo *)Serialiser::AllocAlignedBuffer(sizeof(VkDescriptorInfo)*numElems);
RDCEraseMem(info, sizeof(VkDescriptorInfo)*numElems);
uint32_t e=0;
for(size_t i=0; i < layout.bindings.size(); i++)
for(uint32_t b=0; b < layout.bindings[i].arraySize; b++)
info[e++] = record->descBindings[i][b];
GetResourceManager()->SetInitialContents(id, VulkanResourceManager::InitialContentData(NULL, 0, (byte *)info));
return true;
}
else if(type == eResDeviceMemory)
{
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
RDCASSERT(record->Length > 0);
VkResult vkr = VK_SUCCESS;
VkDevice d = GetDev();
// VKTODOLOW ideally the prepares could be batched up
// a bit more - maybe not all in one command buffer, but
// at least more than one each
VkCmdBuffer cmd = GetNextCmd();
VkDeviceMemory mem = VK_NULL_HANDLE;
VkBufferCreateInfo bufInfo = {
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL,
record->Length, VK_BUFFER_USAGE_TRANSFER_SOURCE_BIT|VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT, 0,
VK_SHARING_MODE_EXCLUSIVE, 0, NULL,
};
// since these are very short lived, they are not wrapped
VkBuffer srcBuf, dstBuf;
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, &srcBuf);
RDCASSERT(vkr == VK_SUCCESS);
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, &dstBuf);
RDCASSERT(vkr == VK_SUCCESS);
VkMemoryRequirements mrq = { 0 };
vkr = ObjDisp(d)->GetBufferMemoryRequirements(Unwrap(d), srcBuf, &mrq);
RDCASSERT(vkr == VK_SUCCESS);
VkMemoryAllocInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO, NULL,
record->Length, GetReadbackMemoryIndex(mrq.memoryTypeBits),
};
allocInfo.allocationSize = AlignUp(allocInfo.allocationSize, mrq.alignment);
vkr = ObjDisp(d)->AllocMemory(Unwrap(d), &allocInfo, &mem);
RDCASSERT(vkr == VK_SUCCESS);
GetResourceManager()->WrapResource(Unwrap(d), mem);
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), srcBuf, ToHandle<VkDeviceMemory>(res), 0);
RDCASSERT(vkr == VK_SUCCESS);
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), dstBuf, Unwrap(mem), 0);
RDCASSERT(vkr == VK_SUCCESS);
VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT };
vkr = ObjDisp(d)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERT(vkr == VK_SUCCESS);
VkBufferCopy region = { 0, 0, record->Length };
ObjDisp(d)->CmdCopyBuffer(Unwrap(cmd), srcBuf, dstBuf, 1, &region);
vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd));
RDCASSERT(vkr == VK_SUCCESS);
// VKTODOLOW would be nice to store up all these buffers so that
// we don't have to submit & flush here before destroying, but
// instead could submit all cmds, then flush once, then destroy
// buffers. (or even not flush at all until capture is over)
SubmitCmds();
FlushQ();
ObjDisp(d)->DestroyBuffer(Unwrap(d), srcBuf);
ObjDisp(d)->DestroyBuffer(Unwrap(d), dstBuf);
GetResourceManager()->SetInitialContents(id, VulkanResourceManager::InitialContentData(GetWrapped(mem), (uint32_t)record->Length, NULL));
return true;
}
else if(type == eResImage)
{
VULKANNOTIMP("image initial states not implemented");
// VKTODOHIGH: need to copy off contents to memory somewhere else
return true;
}
else
{
RDCERR("Unhandled resource type %d", type);
}
return false;
}
bool WrappedVulkan::Serialise_InitialState(WrappedVkRes *res)
{
// use same serialiser as resource manager
Serialiser *localSerialiser = GetMainSerialiser();
SERIALISE_ELEMENT(VkResourceType, type, IdentifyTypeByPtr(res));
SERIALISE_ELEMENT(ResourceId, id, GetResourceManager()->GetID(res));
if(m_State < WRITING) res = GetResourceManager()->GetLiveResource(id);
if(m_State >= WRITING)
{
VulkanResourceManager::InitialContentData initContents = GetResourceManager()->GetInitialContents(id);
if(type == eResDescriptorSet)
{
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
RDCASSERT(record->layout);
const DescSetLayout &layout = *record->layout;
VkDescriptorInfo *info = (VkDescriptorInfo *)initContents.blob;
uint32_t numElems = 0;
for(size_t i=0; i < layout.bindings.size(); i++)
numElems += layout.bindings[i].arraySize;
m_pSerialiser->SerialiseComplexArray("Bindings", info, numElems);
}
else if(type == eResDeviceMemory)
{
VkDevice d = GetDev();
byte *ptr = NULL;
ObjDisp(d)->MapMemory(Unwrap(d), ToHandle<VkDeviceMemory>(initContents.resource), 0, 0, 0, (void **)&ptr);
size_t dataSize = (size_t)initContents.num;
m_pSerialiser->SerialiseBuffer("data", ptr, dataSize);
ObjDisp(d)->UnmapMemory(Unwrap(d), ToHandle<VkDeviceMemory>(initContents.resource));
}
else if(type == eResImage)
{
VULKANNOTIMP("image initial states not implemented");
}
}
else
{
RDCASSERT(res != NULL);
ResourceId liveid = GetResourceManager()->GetLiveID(id);
if(type == eResDescriptorSet)
{
uint32_t numElems;
VkDescriptorInfo *bindings = NULL;
m_pSerialiser->SerialiseComplexArray("Bindings", bindings, numElems);
const DescSetLayout &layout = m_CreationInfo.m_DescSetLayout[ m_DescriptorSetState[liveid].layout ];
uint32_t numBinds = (uint32_t)layout.bindings.size();
// allocate memory to keep the descriptorinfo structures around, as well as a WriteDescriptorSet array
byte *blob = Serialiser::AllocAlignedBuffer(sizeof(VkDescriptorInfo)*numElems + sizeof(VkWriteDescriptorSet)*numBinds);
VkWriteDescriptorSet *writes = (VkWriteDescriptorSet *)blob;
VkDescriptorInfo *info = (VkDescriptorInfo *)(writes + numBinds);
memcpy(info, bindings, sizeof(VkDescriptorInfo)*numElems);
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].destSet = ToHandle<VkDescriptorSet>(res);
writes[i].destBinding = j;
writes[i].destArrayElement = 0;
writes[i].count = layout.bindings[j].arraySize;
writes[i].descriptorType = layout.bindings[j].descriptorType;
writes[i].pDescriptors = info;
info += layout.bindings[j].arraySize;
// 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
bool valid = true;
// quick check for slots that were completely uninitialised
// and so don't have valid data
if(writes[i].pDescriptors->bufferView == VK_NULL_HANDLE &&
writes[i].pDescriptors->sampler == VK_NULL_HANDLE &&
writes[i].pDescriptors->imageView == VK_NULL_HANDLE &&
writes[i].pDescriptors->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].count; d++)
valid &= (writes[i].pDescriptors[d].sampler != VK_NULL_HANDLE);
break;
}
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
{
for(uint32_t d=0; d < writes[i].count; d++)
{
valid &= (writes[i].pDescriptors[d].sampler != VK_NULL_HANDLE);
valid &= (writes[i].pDescriptors[d].imageView != VK_NULL_HANDLE);
}
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].count; d++)
valid &= (writes[i].pDescriptors[d].imageView != VK_NULL_HANDLE);
break;
}
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
{
for(uint32_t d=0; d < writes[i].count; d++)
valid &= (writes[i].pDescriptors[d].bufferView != VK_NULL_HANDLE);
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].count; d++)
valid &= (writes[i].pDescriptors[d].bufferInfo.buffer != VK_NULL_HANDLE);
break;
}
default:
RDCERR("Unexpected descriptor type %d", writes[i].descriptorType);
}
}
// if this write is not valid, skip it
// and start writing the next one in here
if(!valid)
validBinds--;
else
i++;
}
SAFE_DELETE_ARRAY(bindings);
GetResourceManager()->SetInitialContents(id, VulkanResourceManager::InitialContentData(NULL, validBinds, blob));
}
else if(type == eResDeviceMemory)
{
byte *data = NULL;
size_t dataSize = 0;
m_pSerialiser->SerialiseBuffer("data", data, dataSize);
VkResult vkr = VK_SUCCESS;
VkDevice d = GetDev();
VkDeviceMemory mem = VK_NULL_HANDLE;
// VKTODOMED should get mem requirements for buffer - copy might enforce
// some restrictions?
VkMemoryRequirements mrq = { dataSize, 16, ~0U };
VkMemoryAllocInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO, NULL,
dataSize, GetUploadMemoryIndex(mrq.memoryTypeBits),
};
vkr = ObjDisp(d)->AllocMemory(Unwrap(d), &allocInfo, &mem);
RDCASSERT(vkr == VK_SUCCESS);
GetResourceManager()->WrapResource(Unwrap(d), mem);
VkBufferCreateInfo bufInfo = {
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL,
dataSize, VK_BUFFER_USAGE_TRANSFER_SOURCE_BIT|VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT, 0,
VK_SHARING_MODE_EXCLUSIVE, 0, NULL,
};
VkBuffer buf;
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, &buf);
RDCASSERT(vkr == VK_SUCCESS);
GetResourceManager()->WrapResource(Unwrap(d), buf);
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(buf), Unwrap(mem), 0);
RDCASSERT(vkr == VK_SUCCESS);
byte *ptr = NULL;
ObjDisp(d)->MapMemory(Unwrap(d), Unwrap(mem), 0, 0, 0, (void **)&ptr);
// VKTODOLOW could deserialise directly into this ptr if we serialised
// size separately.
memcpy(ptr, data, dataSize);
ObjDisp(d)->UnmapMemory(Unwrap(d), Unwrap(mem));
SAFE_DELETE_ARRAY(data);
m_FreeMems.push_back(mem);
GetResourceManager()->SetInitialContents(id, VulkanResourceManager::InitialContentData(GetWrapped(buf), (uint32_t)dataSize, NULL));
}
else if(type == eResImage)
{
VULKANNOTIMP("image initial states not implemented");
}
}
return true;
}
void WrappedVulkan::Create_InitialState(ResourceId id, WrappedVkRes *live, bool hasData)
{
VkResourceType type = IdentifyTypeByPtr(live);
if(type == eResDescriptorSet)
{
// VKTODOMED need to create some default initial state for descriptor sets.
// if a descriptor set is alloc'd then used in frame we won't have prepared anything,
// but likewise all writes must happen within that frame so the initial state doesn't
// technically matter. We assume the app doesn't try to read from an uninitialised
// descriptor, so for now we can leave the initial state empty.
VULKANNOTIMP("Need to create initial state for descriptor set");
}
else if(type == eResImage)
{
VULKANNOTIMP("image initial states not implemented");
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(NULL, eInitialContents_ClearColorImage, NULL));
return;
}
ImageLayouts &layouts = m_ImageLayouts[liveid];
if(layouts.subresourceStates[0].range.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
GetResourceManager()->SetInitialContents(id, VulkanResourceManager::InitialContentData(NULL, eInitialContents_ClearColorImage, NULL));
else
GetResourceManager()->SetInitialContents(id, VulkanResourceManager::InitialContentData(NULL, eInitialContents_ClearDepthStencilImage, NULL));
}
else if(type == eResDeviceMemory)
{
// ignore, it was probably dirty but not referenced in the frame
}
else if(type == eResFramebuffer)
{
RDCWARN("Framebuffer without initial state! should clear all attachments");
}
else if(type == eResBuffer)
{
// don't have to do anything for buffers, initial state is all handled by memory
}
else
{
RDCERR("Unhandled resource type %d", type);
}
}
void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, VulkanResourceManager::InitialContentData initial)
{
VkResourceType type = IdentifyTypeByPtr(live);
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;
ObjDisp(GetDev())->UpdateDescriptorSets(Unwrap(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<VkDescriptorInfo *> &bindings = m_DescriptorSetState[id].currentBindings;
for(uint32_t i=0; i < initial.num; i++)
{
RDCASSERT(writes[i].destBinding < bindings.size());
RDCASSERT(writes[i].destArrayElement == 0);
VkDescriptorInfo *bind = bindings[writes[i].destBinding];
for(uint32_t d=0; d < writes[i].count; d++)
bind[d] = writes[i].pDescriptors[d];
}
}
else if(type == eResDeviceMemory)
{
VkBuffer srcBuf = (VkBuffer)(uint64_t)initial.resource;
uint32_t memsize = initial.num;
VkDeviceMemory dstMem = (VkDeviceMemory)(uint64_t)live; // maintain the wrapping, for consistency
VkResult vkr = VK_SUCCESS;
VkDevice d = GetDev();
VkCmdBuffer cmd = GetNextCmd();
VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT };
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERT(vkr == VK_SUCCESS);
VkBufferCreateInfo bufInfo = {
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL,
memsize, VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT|VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT, 0,
VK_SHARING_MODE_EXCLUSIVE, 0, NULL,
};
// 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;
// VKTODOMED this should be created once up front, not every time
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, &dstBuf);
RDCASSERT(vkr == VK_SUCCESS);
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), dstBuf, Unwrap(dstMem), 0);
RDCASSERT(vkr == VK_SUCCESS);
VkBufferCopy region = { 0, 0, memsize };
ObjDisp(cmd)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), dstBuf, 1, &region);
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
RDCASSERT(vkr == VK_SUCCESS);
// VKTODOLOW if this dstBuf was persistent or at least cached
// we could batch these command buffers better and wouldn't
// need to flush at all until application of all init states
// is over
SubmitCmds();
FlushQ();
ObjDisp(d)->DestroyBuffer(Unwrap(d), dstBuf);
}
else if(type == eResImage)
{
// VKTODOHIGH: need to copy initial copy to live
VULKANNOTIMP("image initial states not implemented");
}
else
{
RDCERR("Unhandled resource type %d", type);
}
}