mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-26 11:50:59 +00:00
Handle renderpasses with implicit attachment transitions. Closes #212
* We need to make sure we track these transitions, both during capture (so we have the right initial image states at frame begin), and during replay. * Likewise when repurposing a renderpass for isolating a drawcall, we need to make sure the image layouts aren't transitioned - especially from UNDEFINED - otherwise we'd end up with garbage data.
This commit is contained in:
@@ -570,7 +570,10 @@ private:
|
||||
|
||||
void FirstFrame(VkSwapchainKHR swap);
|
||||
|
||||
std::vector<VkImageMemoryBarrier> GetImplicitRenderPassBarriers(uint32_t subpass = 0);
|
||||
string MakeRenderPassOpString(bool store);
|
||||
void MakeSubpassLoadRP(VkRenderPassCreateInfo &info, const VkRenderPassCreateInfo *origInfo,
|
||||
uint32_t s);
|
||||
|
||||
void StartFrameCapture(void *dev, void *wnd);
|
||||
bool EndFrameCapture(void *dev, void *wnd);
|
||||
|
||||
@@ -403,14 +403,7 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan,
|
||||
{
|
||||
attachments.reserve(pCreateInfo->attachmentCount);
|
||||
for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++)
|
||||
{
|
||||
Attachment a;
|
||||
a.loadOp = pCreateInfo->pAttachments[i].loadOp;
|
||||
a.storeOp = pCreateInfo->pAttachments[i].storeOp;
|
||||
a.stencilLoadOp = pCreateInfo->pAttachments[i].stencilLoadOp;
|
||||
a.stencilStoreOp = pCreateInfo->pAttachments[i].stencilStoreOp;
|
||||
attachments.push_back(a);
|
||||
}
|
||||
attachments.push_back(pCreateInfo->pAttachments[i]);
|
||||
|
||||
subpasses.resize(pCreateInfo->subpassCount);
|
||||
for(uint32_t subp = 0; subp < pCreateInfo->subpassCount; subp++)
|
||||
@@ -419,18 +412,30 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan,
|
||||
Subpass &dst = subpasses[subp];
|
||||
|
||||
dst.inputAttachments.resize(src.inputAttachmentCount);
|
||||
dst.inputLayouts.resize(src.inputAttachmentCount);
|
||||
for(uint32_t i = 0; i < src.inputAttachmentCount; i++)
|
||||
{
|
||||
dst.inputAttachments[i] = src.pInputAttachments[i].attachment;
|
||||
dst.inputLayouts[i] = src.pInputAttachments[i].layout;
|
||||
}
|
||||
|
||||
dst.colorAttachments.resize(src.colorAttachmentCount);
|
||||
dst.colorLayouts.resize(src.colorAttachmentCount);
|
||||
for(uint32_t i = 0; i < src.colorAttachmentCount; i++)
|
||||
{
|
||||
dst.colorAttachments[i] = src.pColorAttachments[i].attachment;
|
||||
dst.colorLayouts[i] = src.pColorAttachments[i].layout;
|
||||
}
|
||||
|
||||
dst.depthstencilAttachment =
|
||||
(src.pDepthStencilAttachment != NULL &&
|
||||
src.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED
|
||||
? (int32_t)src.pDepthStencilAttachment->attachment
|
||||
: -1);
|
||||
dst.depthstencilLayout = (src.pDepthStencilAttachment != NULL &&
|
||||
src.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED
|
||||
? src.pDepthStencilAttachment->layout
|
||||
: VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -197,24 +197,25 @@ struct VulkanCreationInfo
|
||||
void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info,
|
||||
const VkRenderPassCreateInfo *pCreateInfo);
|
||||
|
||||
struct Attachment
|
||||
{
|
||||
VkAttachmentLoadOp loadOp;
|
||||
VkAttachmentStoreOp storeOp;
|
||||
VkAttachmentLoadOp stencilLoadOp;
|
||||
VkAttachmentStoreOp stencilStoreOp;
|
||||
};
|
||||
vector<Attachment> attachments;
|
||||
vector<VkAttachmentDescription> attachments;
|
||||
|
||||
struct Subpass
|
||||
{
|
||||
// these are split apart since they layout is
|
||||
// rarely used but the indices are often used
|
||||
vector<uint32_t> inputAttachments;
|
||||
vector<uint32_t> colorAttachments;
|
||||
int32_t depthstencilAttachment;
|
||||
|
||||
vector<VkImageLayout> inputLayouts;
|
||||
vector<VkImageLayout> colorLayouts;
|
||||
VkImageLayout depthstencilLayout;
|
||||
};
|
||||
vector<Subpass> subpasses;
|
||||
|
||||
VkRenderPass loadRP;
|
||||
// one for each subpass, as we preserve attachments
|
||||
// in the layout that the subpass uses
|
||||
vector<VkRenderPass> loadRPs;
|
||||
};
|
||||
map<ResourceId, RenderPass> m_RenderPass;
|
||||
|
||||
|
||||
@@ -5180,11 +5180,15 @@ byte *VulkanReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t m
|
||||
// if we have no tmpImage, we're copying directly from the real image
|
||||
if(tmpImage == VK_NULL_HANDLE)
|
||||
{
|
||||
// ensure transfer has completed
|
||||
srcimBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
|
||||
// image layout back to normal
|
||||
for(size_t si = 0; si < layouts.subresourceStates.size(); si++)
|
||||
{
|
||||
srcimBarrier.subresourceRange = layouts.subresourceStates[si].subresourceRange;
|
||||
srcimBarrier.newLayout = layouts.subresourceStates[si].newLayout;
|
||||
srcimBarrier.dstAccessMask = MakeAccessMask(srcimBarrier.newLayout);
|
||||
DoPipelineBarrier(cmd, 1, &srcimBarrier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,8 +748,8 @@ VkResourceRecord::~VkResourceRecord()
|
||||
if(resType == eResCommandBuffer)
|
||||
SAFE_DELETE(cmdInfo);
|
||||
|
||||
if(resType == eResFramebuffer)
|
||||
SAFE_DELETE(imageAttachments);
|
||||
if(resType == eResFramebuffer || resType == eResRenderPass)
|
||||
SAFE_DELETE_ARRAY(imageAttachments);
|
||||
|
||||
// only the descriptor set layout actually owns this pointer, descriptor sets
|
||||
// have a pointer to it but don't own it
|
||||
|
||||
@@ -871,6 +871,8 @@ struct CmdBufferRecordingInfo
|
||||
VkDevice device;
|
||||
VkCommandBufferAllocateInfo allocInfo;
|
||||
|
||||
VkResourceRecord *framebuffer;
|
||||
|
||||
vector<pair<ResourceId, ImageRegionState> > imgbarriers;
|
||||
|
||||
// sparse resources referenced by this command buffer (at submit time
|
||||
@@ -935,6 +937,17 @@ struct MemMapState
|
||||
byte *refData;
|
||||
};
|
||||
|
||||
struct AttachmentInfo
|
||||
{
|
||||
VkResourceRecord *record;
|
||||
|
||||
// the implicit barrier applied from initialLayout to finalLayout across a render pass
|
||||
// for render passes this is partial (doesn't contain the image pointer), the image
|
||||
// and subresource range are filled in when creating the framebuffer, which is what is
|
||||
// used to apply the barrier in EndRenderPass
|
||||
VkImageMemoryBarrier barrier;
|
||||
};
|
||||
|
||||
struct VkResourceRecord : public ResourceRecord
|
||||
{
|
||||
public:
|
||||
@@ -1046,18 +1059,82 @@ public:
|
||||
SwapchainInfo *swapInfo; // only for swapchains
|
||||
MemMapState *memMapState; // only for device memory
|
||||
CmdBufferRecordingInfo *cmdInfo; // only for command buffers
|
||||
VkResourceRecord **imageAttachments; // only for framebuffers
|
||||
AttachmentInfo *imageAttachments; // only for framebuffers and render passes
|
||||
DescriptorSetData *descInfo; // only for descriptor sets and descriptor set layouts
|
||||
};
|
||||
|
||||
VkResourceRecord *bakedCommands;
|
||||
|
||||
static const int MaxImageAttachments = 9; // 8 Colour and 1 Depth
|
||||
static const int MaxImageAttachments = 17; // 8 Input, 8 Colour and 1 Depth
|
||||
|
||||
// pointer to either the pool this item is allocated from, or the children allocated
|
||||
// from this pool. Protected by the chunk lock
|
||||
VkResourceRecord *pool;
|
||||
vector<VkResourceRecord *> pooledChildren;
|
||||
|
||||
// we only need a couple of bytes to store the view's range,
|
||||
// so just pack/unpack into bitfields
|
||||
struct ViewRange
|
||||
{
|
||||
ViewRange &operator=(const VkImageSubresourceRange &range)
|
||||
{
|
||||
aspectMask = (uint32_t)range.aspectMask;
|
||||
baseMipLevel = range.baseMipLevel;
|
||||
baseArrayLayer = range.baseArrayLayer;
|
||||
|
||||
if(range.levelCount == VK_REMAINING_MIP_LEVELS)
|
||||
levelCount = MipMaxValue;
|
||||
else
|
||||
levelCount = range.levelCount;
|
||||
|
||||
if(range.layerCount == VK_REMAINING_ARRAY_LAYERS)
|
||||
layerCount = SliceMaxValue;
|
||||
else
|
||||
layerCount = range.layerCount;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator VkImageSubresourceRange() const
|
||||
{
|
||||
VkImageSubresourceRange ret;
|
||||
|
||||
ret.aspectMask = (VkImageAspectFlags)aspectMask;
|
||||
ret.baseMipLevel = baseMipLevel;
|
||||
ret.baseArrayLayer = baseArrayLayer;
|
||||
|
||||
if(levelCount == MipMaxValue)
|
||||
ret.levelCount = VK_REMAINING_MIP_LEVELS;
|
||||
else
|
||||
ret.levelCount = levelCount;
|
||||
|
||||
if(layerCount == SliceMaxValue)
|
||||
ret.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
else
|
||||
ret.layerCount = layerCount;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// only need 4 bits for the aspects
|
||||
uint32_t aspectMask : 4;
|
||||
|
||||
// 6 bits = refer to up to 62 mips = bloody huge textures.
|
||||
// note we also need to pack in VK_REMAINING_MIPS etc so we can't
|
||||
// necessarily use the maximum levelCount value (64)
|
||||
uint32_t baseMipLevel : 6;
|
||||
uint32_t levelCount : 6;
|
||||
|
||||
static const uint32_t MipMaxValue = 0x3f;
|
||||
|
||||
// 16 bits = refer to up to 64k array layers. This is less
|
||||
// future proof than above, but at time of writing typical
|
||||
// maxImageArrayLayers is 2048.
|
||||
uint32_t baseArrayLayer : 16;
|
||||
uint32_t layerCount : 16;
|
||||
|
||||
static const uint32_t SliceMaxValue = 0xffff;
|
||||
} viewRange;
|
||||
};
|
||||
|
||||
struct ImageLayouts
|
||||
|
||||
@@ -98,7 +98,7 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd)
|
||||
VkRenderPassBeginInfo rpbegin = {
|
||||
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
NULL,
|
||||
Unwrap(m_CreationInfo->m_RenderPass[renderPass].loadRP),
|
||||
Unwrap(m_CreationInfo->m_RenderPass[renderPass].loadRPs[subpass]),
|
||||
Unwrap(GetResourceManager()->GetCurrentHandle<VkFramebuffer>(framebuffer)),
|
||||
renderArea,
|
||||
(uint32_t)m_CreationInfo->m_RenderPass[renderPass].attachments.size(),
|
||||
@@ -106,9 +106,6 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd)
|
||||
};
|
||||
ObjDisp(cmd)->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
for(uint32_t i = 0; i < subpass; i++)
|
||||
ObjDisp(cmd)->CmdNextSubpass(Unwrap(cmd), VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
BindPipeline(cmd);
|
||||
|
||||
if(ibuffer.buf != ResourceId())
|
||||
@@ -306,10 +303,5 @@ void VulkanRenderState::BindPipeline(VkCommandBuffer cmd)
|
||||
|
||||
void VulkanRenderState::EndRenderPass(VkCommandBuffer cmd)
|
||||
{
|
||||
uint32_t numSubpasses = (uint32_t)m_CreationInfo->m_RenderPass[renderPass].subpasses.size();
|
||||
|
||||
for(uint32_t sub = subpass; sub < numSubpasses - 1; sub++)
|
||||
ObjDisp(cmd)->CmdNextSubpass(Unwrap(cmd), VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
ObjDisp(cmd)->CmdEndRenderPass(Unwrap(cmd));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,155 @@
|
||||
|
||||
#include "../vk_core.h"
|
||||
|
||||
std::vector<VkImageMemoryBarrier> WrappedVulkan::GetImplicitRenderPassBarriers(uint32_t subpass)
|
||||
{
|
||||
ResourceId rp, fb;
|
||||
|
||||
if(m_State == EXECUTING)
|
||||
{
|
||||
rp = m_RenderState.renderPass;
|
||||
fb = m_RenderState.framebuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
rp = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.renderPass;
|
||||
fb = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer;
|
||||
}
|
||||
|
||||
std::vector<VkImageMemoryBarrier> ret;
|
||||
|
||||
VulkanCreationInfo::Framebuffer fbinfo = m_CreationInfo.m_Framebuffer[fb];
|
||||
VulkanCreationInfo::RenderPass rpinfo = m_CreationInfo.m_RenderPass[rp];
|
||||
|
||||
std::vector<VkAttachmentReference> atts;
|
||||
|
||||
// a bit of dancing to get a subpass index. Because we don't increment
|
||||
// the subpass counter on EndRenderPass the value is the same for the last
|
||||
// NextSubpass. Instead we pass in the subpass index of ~0U for End
|
||||
if(subpass == ~0U)
|
||||
{
|
||||
// we transition all attachments to finalLayout from whichever they
|
||||
// were in previously
|
||||
atts.resize(rpinfo.attachments.size());
|
||||
for(size_t i = 0; i < rpinfo.attachments.size(); i++)
|
||||
{
|
||||
atts[i].attachment = (uint32_t)i;
|
||||
atts[i].layout = rpinfo.attachments[i].finalLayout;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_State == EXECUTING)
|
||||
subpass = m_RenderState.subpass;
|
||||
else
|
||||
subpass = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass;
|
||||
|
||||
// transition the attachments in this subpass
|
||||
for(size_t i = 0; i < rpinfo.subpasses[subpass].colorAttachments.size(); i++)
|
||||
{
|
||||
atts.push_back(VkAttachmentReference());
|
||||
atts.back().attachment = rpinfo.subpasses[subpass].colorAttachments[i];
|
||||
atts.back().layout = rpinfo.subpasses[subpass].colorLayouts[i];
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < rpinfo.subpasses[subpass].inputAttachments.size(); i++)
|
||||
{
|
||||
atts.push_back(VkAttachmentReference());
|
||||
atts.back().attachment = rpinfo.subpasses[subpass].inputAttachments[i];
|
||||
atts.back().layout = rpinfo.subpasses[subpass].inputLayouts[i];
|
||||
}
|
||||
|
||||
int32_t ds = rpinfo.subpasses[subpass].depthstencilAttachment;
|
||||
|
||||
if(ds != -1)
|
||||
{
|
||||
atts.push_back(VkAttachmentReference());
|
||||
atts.back().attachment = (uint32_t)rpinfo.subpasses[subpass].depthstencilAttachment;
|
||||
atts.back().layout = rpinfo.subpasses[subpass].depthstencilLayout;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < atts.size(); i++)
|
||||
{
|
||||
uint32_t idx = atts[i].attachment;
|
||||
|
||||
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
||||
|
||||
ResourceId view = fbinfo.attachments[idx].view;
|
||||
|
||||
barrier.subresourceRange = m_CreationInfo.m_ImageView[view].range;
|
||||
barrier.image = Unwrap(
|
||||
GetResourceManager()->GetCurrentHandle<VkImage>(m_CreationInfo.m_ImageView[view].image));
|
||||
|
||||
barrier.newLayout = atts[i].layout;
|
||||
|
||||
// search back from this subpass to see which layout it was in before. If it's
|
||||
// not been used in a previous subpass, then default to initialLayout
|
||||
barrier.oldLayout = rpinfo.attachments[idx].initialLayout;
|
||||
|
||||
if(subpass == ~0U)
|
||||
subpass = (uint32_t)rpinfo.subpasses.size();
|
||||
|
||||
// subpass is at this point a 1-indexed value essentially, as it's the index
|
||||
// of the subpass we just finished (or 0 if we're in BeginRenderPass in which
|
||||
// case the loop just skips completely and we use initialLayout, which is
|
||||
// correct).
|
||||
|
||||
for(uint32_t s = subpass; s > 0; s--)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
for(size_t a = 0; !found && a < rpinfo.subpasses[s - 1].colorAttachments.size(); a++)
|
||||
{
|
||||
if(rpinfo.subpasses[s - 1].colorAttachments[a] == idx)
|
||||
{
|
||||
barrier.oldLayout = rpinfo.subpasses[s - 1].colorLayouts[a];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found)
|
||||
break;
|
||||
|
||||
for(size_t a = 0; !found && a < rpinfo.subpasses[s - 1].inputAttachments.size(); a++)
|
||||
{
|
||||
if(rpinfo.subpasses[s - 1].inputAttachments[a] == idx)
|
||||
{
|
||||
barrier.oldLayout = rpinfo.subpasses[s - 1].inputLayouts[a];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found)
|
||||
break;
|
||||
|
||||
if((uint32_t)rpinfo.subpasses[s - 1].depthstencilAttachment == idx)
|
||||
{
|
||||
barrier.oldLayout = rpinfo.subpasses[s - 1].depthstencilLayout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReplacePresentableImageLayout(barrier.oldLayout);
|
||||
ReplacePresentableImageLayout(barrier.newLayout);
|
||||
|
||||
ret.push_back(barrier);
|
||||
}
|
||||
|
||||
// erase any do-nothing barriers
|
||||
for(auto it = ret.begin(); it != ret.end();)
|
||||
{
|
||||
if(it->oldLayout == it->newLayout)
|
||||
it = ret.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
string WrappedVulkan::MakeRenderPassOpString(bool store)
|
||||
{
|
||||
string opDesc = "";
|
||||
@@ -33,7 +182,7 @@ string WrappedVulkan::MakeRenderPassOpString(bool store)
|
||||
const VulkanCreationInfo::Framebuffer &fbinfo =
|
||||
m_CreationInfo.m_Framebuffer[m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer];
|
||||
|
||||
const vector<VulkanCreationInfo::RenderPass::Attachment> &atts = info.attachments;
|
||||
const vector<VkAttachmentDescription> &atts = info.attachments;
|
||||
|
||||
if(atts.empty())
|
||||
{
|
||||
@@ -680,6 +829,12 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(Serialiser *localSerialiser,
|
||||
m_RenderState.renderPass = GetResourceManager()->GetNonDispWrapper(beginInfo.renderPass)->id;
|
||||
m_RenderState.framebuffer = GetResourceManager()->GetNonDispWrapper(beginInfo.framebuffer)->id;
|
||||
m_RenderState.renderArea = beginInfo.renderArea;
|
||||
|
||||
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
|
||||
|
||||
ResourceId cmd = GetResID(commandBuffer);
|
||||
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
}
|
||||
}
|
||||
else if(m_State == READING)
|
||||
@@ -695,6 +850,12 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(Serialiser *localSerialiser,
|
||||
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer =
|
||||
GetResourceManager()->GetNonDispWrapper(beginInfo.framebuffer)->id;
|
||||
|
||||
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
|
||||
|
||||
ResourceId cmd = GetResID(commandBuffer);
|
||||
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
|
||||
const string desc = localSerialiser->GetDebugStr();
|
||||
|
||||
string opDesc = MakeRenderPassOpString(false);
|
||||
@@ -738,16 +899,19 @@ void WrappedVulkan::vkCmdBeginRenderPass(VkCommandBuffer commandBuffer,
|
||||
record->MarkResourceFrameReferenced(fb->GetResourceID(), eFrameRef_Read);
|
||||
for(size_t i = 0; i < VkResourceRecord::MaxImageAttachments; i++)
|
||||
{
|
||||
if(fb->imageAttachments[i] == NULL)
|
||||
VkResourceRecord *att = fb->imageAttachments[i].record;
|
||||
if(att == NULL)
|
||||
break;
|
||||
|
||||
record->MarkResourceFrameReferenced(fb->imageAttachments[i]->baseResource, eFrameRef_Write);
|
||||
if(fb->imageAttachments[i]->baseResourceMem != ResourceId())
|
||||
record->MarkResourceFrameReferenced(fb->imageAttachments[i]->baseResourceMem, eFrameRef_Read);
|
||||
if(fb->imageAttachments[i]->sparseInfo)
|
||||
record->cmdInfo->sparse.insert(fb->imageAttachments[i]->sparseInfo);
|
||||
record->cmdInfo->dirtied.insert(fb->imageAttachments[i]->baseResource);
|
||||
record->MarkResourceFrameReferenced(att->baseResource, eFrameRef_Write);
|
||||
if(att->baseResourceMem != ResourceId())
|
||||
record->MarkResourceFrameReferenced(att->baseResourceMem, eFrameRef_Read);
|
||||
if(att->sparseInfo)
|
||||
record->cmdInfo->sparse.insert(att->sparseInfo);
|
||||
record->cmdInfo->dirtied.insert(att->baseResource);
|
||||
}
|
||||
|
||||
record->cmdInfo->framebuffer = fb;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,6 +936,12 @@ bool WrappedVulkan::Serialise_vkCmdNextSubpass(Serialiser *localSerialiser,
|
||||
m_RenderState.subpass++;
|
||||
|
||||
ObjDisp(commandBuffer)->CmdNextSubpass(Unwrap(commandBuffer), cont);
|
||||
|
||||
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
|
||||
|
||||
ResourceId cmd = GetResID(commandBuffer);
|
||||
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
}
|
||||
}
|
||||
else if(m_State == READING)
|
||||
@@ -783,6 +953,12 @@ bool WrappedVulkan::Serialise_vkCmdNextSubpass(Serialiser *localSerialiser,
|
||||
// track while reading, for fetching the right set of outputs in AddDrawcall
|
||||
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass++;
|
||||
|
||||
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
|
||||
|
||||
ResourceId cmd = GetResID(commandBuffer);
|
||||
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
|
||||
const string desc = localSerialiser->GetDebugStr();
|
||||
|
||||
AddEvent(NEXT_SUBPASS, desc);
|
||||
@@ -833,6 +1009,12 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass(Serialiser *localSerialiser,
|
||||
|
||||
m_Partial[Primary].renderPassActive = false;
|
||||
ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer));
|
||||
|
||||
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers(~0U);
|
||||
|
||||
ResourceId cmd = GetResID(commandBuffer);
|
||||
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
}
|
||||
}
|
||||
else if(m_State == READING)
|
||||
@@ -841,6 +1023,12 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass(Serialiser *localSerialiser,
|
||||
|
||||
ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer));
|
||||
|
||||
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers(~0U);
|
||||
|
||||
ResourceId cmd = GetResID(commandBuffer);
|
||||
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
|
||||
const string desc = localSerialiser->GetDebugStr();
|
||||
|
||||
string opDesc = MakeRenderPassOpString(true);
|
||||
@@ -877,6 +1065,25 @@ void WrappedVulkan::vkCmdEndRenderPass(VkCommandBuffer commandBuffer)
|
||||
Serialise_vkCmdEndRenderPass(localSerialiser, commandBuffer);
|
||||
|
||||
record->AddChunk(scope.Get());
|
||||
|
||||
VkResourceRecord *fb = record->cmdInfo->framebuffer;
|
||||
|
||||
std::vector<VkImageMemoryBarrier> barriers;
|
||||
|
||||
for(size_t i = 0; i < VkResourceRecord::MaxImageAttachments; i++)
|
||||
{
|
||||
if(fb->imageAttachments[i].barrier.oldLayout == fb->imageAttachments[i].barrier.newLayout)
|
||||
continue;
|
||||
|
||||
barriers.push_back(fb->imageAttachments[i].barrier);
|
||||
}
|
||||
|
||||
// apply the implicit layout transitions here
|
||||
{
|
||||
SCOPED_LOCK(m_ImageLayoutsLock);
|
||||
GetResourceManager()->RecordBarriers(GetRecord(commandBuffer)->cmdInfo->imgbarriers,
|
||||
m_ImageLayouts, (uint32_t)barriers.size(), &barriers[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1635,7 +1842,7 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier(
|
||||
(uint32_t)bufBarriers.size(), &bufBarriers[0],
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
|
||||
ResourceId cmd = GetResID(RerecordCmdBuf(cmdid));
|
||||
ResourceId cmd = GetResID(commandBuffer);
|
||||
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
|
||||
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,40 @@
|
||||
|
||||
#include "../vk_core.h"
|
||||
|
||||
void WrappedVulkan::MakeSubpassLoadRP(VkRenderPassCreateInfo &info,
|
||||
const VkRenderPassCreateInfo *origInfo, uint32_t s)
|
||||
{
|
||||
info.subpassCount = 1;
|
||||
info.pSubpasses = origInfo->pSubpasses + s;
|
||||
|
||||
// remove any dependencies
|
||||
info.dependencyCount = 0;
|
||||
|
||||
const VkSubpassDescription *sub = info.pSubpasses;
|
||||
VkAttachmentDescription *att = (VkAttachmentDescription *)info.pAttachments;
|
||||
|
||||
// apply this subpass's attachment layouts to the initial and final layouts
|
||||
// so that this RP doesn't perform any layout transitions
|
||||
for(uint32_t a = 0; a < sub->colorAttachmentCount; a++)
|
||||
{
|
||||
att[sub->pColorAttachments[a].attachment].initialLayout =
|
||||
att[sub->pColorAttachments[a].attachment].finalLayout = sub->pColorAttachments[a].layout;
|
||||
}
|
||||
|
||||
for(uint32_t a = 0; a < sub->inputAttachmentCount; a++)
|
||||
{
|
||||
att[sub->pInputAttachments[a].attachment].initialLayout =
|
||||
att[sub->pInputAttachments[a].attachment].finalLayout = sub->pInputAttachments[a].layout;
|
||||
}
|
||||
|
||||
if(sub->pDepthStencilAttachment && sub->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)
|
||||
{
|
||||
att[sub->pDepthStencilAttachment->attachment].initialLayout =
|
||||
att[sub->pDepthStencilAttachment->attachment].finalLayout =
|
||||
sub->pDepthStencilAttachment->layout;
|
||||
}
|
||||
}
|
||||
|
||||
// note, for threading reasons we ensure to release the wrappers before
|
||||
// releasing the underlying object. Otherwise after releasing the vulkan object
|
||||
// that same handle could be returned by create on another thread, and we
|
||||
@@ -501,20 +535,26 @@ VkResult WrappedVulkan::vkCreateFramebuffer(VkDevice device,
|
||||
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pFramebuffer);
|
||||
record->AddChunk(chunk);
|
||||
|
||||
record->imageAttachments = new VkResourceRecord *[VkResourceRecord::MaxImageAttachments];
|
||||
record->imageAttachments = new AttachmentInfo[VkResourceRecord::MaxImageAttachments];
|
||||
RDCASSERT(pCreateInfo->attachmentCount <= VkResourceRecord::MaxImageAttachments);
|
||||
|
||||
RDCEraseMem(record->imageAttachments,
|
||||
sizeof(VkResourceRecord *) * VkResourceRecord::MaxImageAttachments);
|
||||
sizeof(AttachmentInfo) * VkResourceRecord::MaxImageAttachments);
|
||||
|
||||
VkResourceRecord *rpRecord = GetRecord(pCreateInfo->renderPass);
|
||||
|
||||
record->AddParent(rpRecord);
|
||||
|
||||
if(pCreateInfo->renderPass != VK_NULL_HANDLE)
|
||||
record->AddParent(GetRecord(pCreateInfo->renderPass));
|
||||
for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++)
|
||||
{
|
||||
VkResourceRecord *attRecord = GetRecord(pCreateInfo->pAttachments[i]);
|
||||
record->AddParent(attRecord);
|
||||
|
||||
record->imageAttachments[i] = attRecord;
|
||||
record->imageAttachments[i].record = attRecord;
|
||||
record->imageAttachments[i].barrier = rpRecord->imageAttachments[i].barrier;
|
||||
record->imageAttachments[i].barrier.image =
|
||||
GetResourceManager()->GetCurrentHandle<VkImage>(attRecord->baseResource);
|
||||
record->imageAttachments[i].barrier.subresourceRange = attRecord->viewRange;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -547,12 +587,20 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass(Serialiser *localSerialiser, Vk
|
||||
|
||||
// we want to store off the data so we can display it after the pass.
|
||||
// override any user-specified DONT_CARE.
|
||||
// Likewise we don't want to throw away data before we're ready, so change
|
||||
// any load ops to LOAD instead of DONT_CARE (which is valid!). We of course
|
||||
// leave any LOAD_OP_CLEAR alone.
|
||||
VkAttachmentDescription *att = (VkAttachmentDescription *)info.pAttachments;
|
||||
for(uint32_t i = 0; i < info.attachmentCount; i++)
|
||||
{
|
||||
att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
|
||||
if(att[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE)
|
||||
att[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
if(att[i].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE)
|
||||
att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
|
||||
// renderpass can't start or end in presentable layout on replay
|
||||
ReplacePresentableImageLayout(att[i].initialLayout);
|
||||
ReplacePresentableImageLayout(att[i].finalLayout);
|
||||
@@ -593,28 +641,39 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass(Serialiser *localSerialiser, Vk
|
||||
att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
}
|
||||
|
||||
ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRP);
|
||||
RDCASSERTEQUAL(ret, VK_SUCCESS);
|
||||
VkRenderPassCreateInfo loadInfo = info;
|
||||
|
||||
// handle the loadRP being a duplicate
|
||||
if(GetResourceManager()->HasWrapper(ToTypedHandle(rpinfo.loadRP)))
|
||||
rpinfo.loadRPs.resize(info.subpassCount);
|
||||
|
||||
// create a render pass for each subpass that maintains attachment layouts
|
||||
for(uint32_t s = 0; s < info.subpassCount; s++)
|
||||
{
|
||||
// just fetch the existing wrapped object
|
||||
rpinfo.loadRP =
|
||||
(VkRenderPass)(uint64_t)GetResourceManager()->GetNonDispWrapper(rpinfo.loadRP);
|
||||
MakeSubpassLoadRP(loadInfo, &info, s);
|
||||
|
||||
// destroy this instance of the duplicate, as we must have matching create/destroy
|
||||
// calls and there won't be a wrapped resource hanging around to destroy this one.
|
||||
ObjDisp(device)->DestroyRenderPass(Unwrap(device), rpinfo.loadRP, NULL);
|
||||
ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRPs[s]);
|
||||
RDCASSERTEQUAL(ret, VK_SUCCESS);
|
||||
|
||||
// don't need to ReplaceResource as no IDs are involved
|
||||
}
|
||||
else
|
||||
{
|
||||
ResourceId loadRPid = GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRP);
|
||||
// handle the loadRP being a duplicate
|
||||
if(GetResourceManager()->HasWrapper(ToTypedHandle(rpinfo.loadRPs[s])))
|
||||
{
|
||||
// just fetch the existing wrapped object
|
||||
rpinfo.loadRPs[s] =
|
||||
(VkRenderPass)(uint64_t)GetResourceManager()->GetNonDispWrapper(rpinfo.loadRPs[s]);
|
||||
|
||||
// register as a live-only resource, so it is cleaned up properly
|
||||
GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRP);
|
||||
// destroy this instance of the duplicate, as we must have matching create/destroy
|
||||
// calls and there won't be a wrapped resource hanging around to destroy this one.
|
||||
ObjDisp(device)->DestroyRenderPass(Unwrap(device), rpinfo.loadRPs[s], NULL);
|
||||
|
||||
// don't need to ReplaceResource as no IDs are involved
|
||||
}
|
||||
else
|
||||
{
|
||||
ResourceId loadRPid =
|
||||
GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRPs[s]);
|
||||
|
||||
// register as a live-only resource, so it is cleaned up properly
|
||||
GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRPs[s]);
|
||||
}
|
||||
}
|
||||
|
||||
m_CreationInfo.m_RenderPass[live] = rpinfo;
|
||||
@@ -651,6 +710,20 @@ VkResult WrappedVulkan::vkCreateRenderPass(VkDevice device, const VkRenderPassCr
|
||||
|
||||
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pRenderPass);
|
||||
record->AddChunk(chunk);
|
||||
|
||||
record->imageAttachments = new AttachmentInfo[VkResourceRecord::MaxImageAttachments];
|
||||
RDCASSERT(pCreateInfo->attachmentCount <= VkResourceRecord::MaxImageAttachments);
|
||||
|
||||
RDCEraseMem(record->imageAttachments,
|
||||
sizeof(AttachmentInfo) * VkResourceRecord::MaxImageAttachments);
|
||||
|
||||
for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++)
|
||||
{
|
||||
record->imageAttachments[i].record = NULL;
|
||||
record->imageAttachments[i].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
record->imageAttachments[i].barrier.oldLayout = pCreateInfo->pAttachments[i].initialLayout;
|
||||
record->imageAttachments[i].barrier.newLayout = pCreateInfo->pAttachments[i].finalLayout;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -676,13 +749,21 @@ VkResult WrappedVulkan::vkCreateRenderPass(VkDevice device, const VkRenderPassCr
|
||||
|
||||
info.pAttachments = atts;
|
||||
|
||||
ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRP);
|
||||
RDCASSERTEQUAL(ret, VK_SUCCESS);
|
||||
rpinfo.loadRPs.resize(pCreateInfo->subpassCount);
|
||||
|
||||
ResourceId loadRPid = GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRP);
|
||||
// create a render pass for each subpass that maintains attachment layouts
|
||||
for(uint32_t s = 0; s < pCreateInfo->subpassCount; s++)
|
||||
{
|
||||
MakeSubpassLoadRP(info, pCreateInfo, s);
|
||||
|
||||
// register as a live-only resource, so it is cleaned up properly
|
||||
GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRP);
|
||||
ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &info, NULL, &rpinfo.loadRPs[s]);
|
||||
RDCASSERTEQUAL(ret, VK_SUCCESS);
|
||||
|
||||
ResourceId loadRPid = GetResourceManager()->WrapResource(Unwrap(device), rpinfo.loadRPs[s]);
|
||||
|
||||
// register as a live-only resource, so it is cleaned up properly
|
||||
GetResourceManager()->AddLiveResource(loadRPid, rpinfo.loadRPs[s]);
|
||||
}
|
||||
|
||||
m_CreationInfo.m_RenderPass[id] = rpinfo;
|
||||
}
|
||||
|
||||
@@ -1254,6 +1254,7 @@ VkResult WrappedVulkan::vkCreateImageView(VkDevice device, const VkImageViewCrea
|
||||
record->baseResource = imageRecord->GetResourceID();
|
||||
record->baseResourceMem = imageRecord->baseResource;
|
||||
record->sparseInfo = imageRecord->sparseInfo;
|
||||
record->viewRange = pCreateInfo->subresourceRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user