bool WrappedVulkan::Serialise_vkCreateImage( Serialiser* localSerialiser, VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage) { SERIALISE_ELEMENT(ResourceId, devId, GetResID(device)); SERIALISE_ELEMENT(VkImageCreateInfo, info, *pCreateInfo); SERIALISE_ELEMENT(ResourceId, id, GetResID(*pImage)); if(m_State == READING) { device = GetResourceManager()->GetLiveHandle<VkDevice>(devId); VkImage img = VK_NULL_HANDLE; VkImageUsageFlags origusage = info.usage; // ensure we can always display and copy from/to textures info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT; VkResult ret = ObjDisp(device)->CreateImage(Unwrap(device), &info, NULL, &img); info.usage = origusage; if(ret != VK_SUCCESS) { RDCERR("Failed on resource serialise-creation, VkResult: 0x%08x", ret); } else { ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), img); GetResourceManager()->AddLiveResource(id, img); m_CreationInfo.m_Image[live].Init(GetResourceManager(), m_CreationInfo, &info); VkImageSubresourceRange range; range.baseMipLevel = range.baseArrayLayer = 0; range.levelCount = info.mipLevels; range.layerCount = info.arrayLayers; if(info.imageType == VK_IMAGE_TYPE_3D) range.layerCount = info.extent.depth; ImageLayouts &layouts = m_ImageLayouts[live]; layouts.subresourceStates.clear(); layouts.layerCount = info.arrayLayers; layouts.levelCount = info.mipLevels; layouts.extent = info.extent; layouts.format = info.format; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; if(IsDepthOnlyFormat(info.format)) range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; else if(IsDepthStencilFormat(info.format)) range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT|VK_IMAGE_ASPECT_STENCIL_BIT; layouts.subresourceStates.push_back(ImageRegionState(range, UNKNOWN_PREV_IMG_LAYOUT, VK_IMAGE_LAYOUT_UNDEFINED)); } } return true; }
VkResult WrappedVulkan::vkCreateImage( VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage) { VkImageCreateInfo createInfo_adjusted = *pCreateInfo; createInfo_adjusted.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; VkResult ret = ObjDisp(device)->CreateImage(Unwrap(device), &createInfo_adjusted, pAllocator, pImage); // SHARING: pCreateInfo sharingMode, queueFamilyCount, pQueueFamilyIndices if(ret == VK_SUCCESS) { ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pImage); if(m_State >= WRITING) { Chunk *chunk = NULL; { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(CREATE_IMAGE); Serialise_vkCreateImage(localSerialiser, device, pCreateInfo, NULL, pImage); chunk = scope.Get(); } VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pImage); record->AddChunk(chunk); if(pCreateInfo->flags & (VK_IMAGE_CREATE_SPARSE_BINDING_BIT|VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT)) { record->sparseInfo = new SparseMapping(); { SCOPED_LOCK(m_CapTransitionLock); if(m_State != WRITING_CAPFRAME) GetResourceManager()->MarkDirtyResource(id); else GetResourceManager()->MarkPendingDirty(id); } if(pCreateInfo->flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) { // must record image and page dimension, and create page tables uint32_t numreqs = NUM_VK_IMAGE_ASPECTS; VkSparseImageMemoryRequirements reqs[NUM_VK_IMAGE_ASPECTS]; ObjDisp(device)->GetImageSparseMemoryRequirements(Unwrap(device), Unwrap(*pImage), &numreqs, reqs); RDCASSERT(numreqs > 0); record->sparseInfo->pagedim = reqs[0].formatProperties.imageGranularity; record->sparseInfo->imgdim = pCreateInfo->extent; record->sparseInfo->imgdim.width /= record->sparseInfo->pagedim.width; record->sparseInfo->imgdim.height /= record->sparseInfo->pagedim.height; record->sparseInfo->imgdim.depth /= record->sparseInfo->pagedim.depth; uint32_t numpages = record->sparseInfo->imgdim.width*record->sparseInfo->imgdim.height*record->sparseInfo->imgdim.depth; for(uint32_t i=0; i < numreqs; i++) { // assume all page sizes are the same for all aspects RDCASSERT(record->sparseInfo->pagedim.width == reqs[i].formatProperties.imageGranularity.width && record->sparseInfo->pagedim.height == reqs[i].formatProperties.imageGranularity.height && record->sparseInfo->pagedim.depth == reqs[i].formatProperties.imageGranularity.depth); int a=0; for(; a < NUM_VK_IMAGE_ASPECTS; a++) if(reqs[i].formatProperties.aspectMask & (1<<a)) break; record->sparseInfo->pages[a] = new pair<VkDeviceMemory, VkDeviceSize>[numpages]; } } else { // don't have to do anything, image is opaque and must be fully bound, just need // to track the memory bindings. } } } else { GetResourceManager()->AddLiveResource(id, *pImage); m_CreationInfo.m_Image[id].Init(GetResourceManager(), m_CreationInfo, pCreateInfo); } VkImageSubresourceRange range; range.baseMipLevel = range.baseArrayLayer = 0; range.levelCount = pCreateInfo->mipLevels; range.layerCount = pCreateInfo->arrayLayers; if(pCreateInfo->imageType == VK_IMAGE_TYPE_3D) range.layerCount = pCreateInfo->extent.depth; ImageLayouts *layout = NULL; { SCOPED_LOCK(m_ImageLayoutsLock); layout = &m_ImageLayouts[id]; } layout->layerCount = pCreateInfo->arrayLayers; layout->levelCount = pCreateInfo->mipLevels; layout->extent = pCreateInfo->extent; layout->format = pCreateInfo->format; layout->subresourceStates.clear(); range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; if(IsDepthOnlyFormat(pCreateInfo->format)) range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; else if(IsDepthStencilFormat(pCreateInfo->format)) range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT|VK_IMAGE_ASPECT_STENCIL_BIT; layout->subresourceStates.push_back(ImageRegionState(range, UNKNOWN_PREV_IMG_LAYOUT, VK_IMAGE_LAYOUT_UNDEFINED)); } return ret; }
bool WrappedVulkan::Serialise_vkCreateSwapchainKHR( Serialiser* localSerialiser, VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapChain) { SERIALISE_ELEMENT(ResourceId, devId, GetResID(device)); SERIALISE_ELEMENT(VkSwapchainCreateInfoKHR, info, *pCreateInfo); SERIALISE_ELEMENT(ResourceId, id, GetResID(*pSwapChain)); uint32_t numIms = 0; if(m_State >= WRITING) { VkResult vkr = VK_SUCCESS; vkr = ObjDisp(device)->GetSwapchainImagesKHR(Unwrap(device), Unwrap(*pSwapChain), &numIms, NULL); RDCASSERTEQUAL(vkr, VK_SUCCESS); } SERIALISE_ELEMENT(uint32_t, numSwapImages, numIms); SERIALISE_ELEMENT(VkSharingMode, sharingMode, pCreateInfo->imageSharingMode); if(m_State == READING) { // use original ID because we don't create a live version of the swapchain SwapchainInfo &swapinfo = m_CreationInfo.m_SwapChain[id]; swapinfo.format = info.imageFormat; swapinfo.extent = info.imageExtent; swapinfo.arraySize = info.imageArrayLayers; swapinfo.images.resize(numSwapImages); device = GetResourceManager()->GetLiveHandle<VkDevice>(devId); const VkImageCreateInfo imInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, VK_IMAGE_TYPE_2D, info.imageFormat, { info.imageExtent.width, info.imageExtent.height, 1 }, 1, info.imageArrayLayers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT| VK_IMAGE_USAGE_TRANSFER_DST_BIT| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT| VK_IMAGE_USAGE_SAMPLED_BIT, sharingMode, 0, NULL, VK_IMAGE_LAYOUT_UNDEFINED, }; for(uint32_t i=0; i < numSwapImages; i++) { VkDeviceMemory mem = VK_NULL_HANDLE; VkImage im = VK_NULL_HANDLE; VkResult vkr = ObjDisp(device)->CreateImage(Unwrap(device), &imInfo, NULL, &im); RDCASSERTEQUAL(vkr, VK_SUCCESS); ResourceId liveId = GetResourceManager()->WrapResource(Unwrap(device), im); VkMemoryRequirements mrq = {0}; ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), Unwrap(im), &mrq); VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size, GetGPULocalMemoryIndex(mrq.memoryTypeBits), }; vkr = ObjDisp(device)->AllocateMemory(Unwrap(device), &allocInfo, NULL, &mem); RDCASSERTEQUAL(vkr, VK_SUCCESS); ResourceId memid = GetResourceManager()->WrapResource(Unwrap(device), mem); // register as a live-only resource, so it is cleaned up properly GetResourceManager()->AddLiveResource(memid, mem); vkr = ObjDisp(device)->BindImageMemory(Unwrap(device), Unwrap(im), Unwrap(mem), 0); RDCASSERTEQUAL(vkr, VK_SUCCESS); // image live ID will be assigned separately in Serialise_vkGetSwapChainInfoWSI // memory doesn't have a live ID swapinfo.images[i].im = im; // fill out image info so we track resource state barriers // sneaky-cheeky use of the swapchain's ID here (it's not a live ID because // we don't create a live swapchain). This will be picked up in // Serialise_vkGetSwapchainImagesKHR to set the data for the live IDs on the // swapchain images. VulkanCreationInfo::Image &iminfo = m_CreationInfo.m_Image[id]; iminfo.type = VK_IMAGE_TYPE_2D; iminfo.format = info.imageFormat; iminfo.extent.width = info.imageExtent.width; iminfo.extent.height = info.imageExtent.height; iminfo.extent.depth = 1; iminfo.mipLevels = 1; iminfo.arrayLayers = info.imageArrayLayers; iminfo.creationFlags = eTextureCreate_SRV|eTextureCreate_RTV|eTextureCreate_SwapBuffer; iminfo.cube = false; iminfo.samples = VK_SAMPLE_COUNT_1_BIT; m_CreationInfo.m_Names[liveId] = StringFormat::Fmt("Presentable Image %u", i); VkImageSubresourceRange range; range.baseMipLevel = range.baseArrayLayer = 0; range.levelCount = 1; range.layerCount = info.imageArrayLayers; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; m_ImageLayouts[liveId].subresourceStates.clear(); m_ImageLayouts[liveId].subresourceStates.push_back(ImageRegionState(range, UNKNOWN_PREV_IMG_LAYOUT, VK_IMAGE_LAYOUT_UNDEFINED)); } } return true; }
VkResult WrappedVulkan::vkCreateSwapchainKHR( VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapChain) { VkSwapchainCreateInfoKHR createInfo = *pCreateInfo; // make sure we can readback to get the screenshot createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; createInfo.surface = Unwrap(createInfo.surface); createInfo.oldSwapchain = Unwrap(createInfo.oldSwapchain); VkResult ret = ObjDisp(device)->CreateSwapchainKHR(Unwrap(device), &createInfo, pAllocator, pSwapChain); if(ret == VK_SUCCESS) { ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pSwapChain); if(m_State >= WRITING) { Chunk *chunk = NULL; { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(CREATE_SWAP_BUFFER); Serialise_vkCreateSwapchainKHR(localSerialiser, device, pCreateInfo, NULL, pSwapChain); chunk = scope.Get(); } VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pSwapChain); record->AddChunk(chunk); record->swapInfo = new SwapchainInfo(); SwapchainInfo &swapInfo = *record->swapInfo; // sneaky casting of window handle into record swapInfo.wndHandle = (RENDERDOC_WindowHandle)GetRecord(pCreateInfo->surface); { SCOPED_LOCK(m_SwapLookupLock); m_SwapLookup[swapInfo.wndHandle] = *pSwapChain; } RenderDoc::Inst().AddFrameCapturer(LayerDisp(m_Instance), swapInfo.wndHandle, this); swapInfo.format = pCreateInfo->imageFormat; swapInfo.extent = pCreateInfo->imageExtent; swapInfo.arraySize = pCreateInfo->imageArrayLayers; VkResult vkr = VK_SUCCESS; const VkLayerDispatchTable *vt = ObjDisp(device); { VkAttachmentDescription attDesc = { 0, pCreateInfo->imageFormat, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkAttachmentReference attRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; VkSubpassDescription sub = { 0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, NULL, // inputs 1, &attRef, // color NULL, // resolve NULL, // depth-stencil 0, NULL, // preserve }; VkRenderPassCreateInfo rpinfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub, 0, NULL, // dependencies }; vkr = vt->CreateRenderPass(Unwrap(device), &rpinfo, NULL, &swapInfo.rp); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapInfo.rp); } // serialise out the swap chain images { uint32_t numSwapImages; VkResult ret = vt->GetSwapchainImagesKHR(Unwrap(device), Unwrap(*pSwapChain), &numSwapImages, NULL); RDCASSERTEQUAL(ret, VK_SUCCESS); swapInfo.lastPresent = 0; swapInfo.images.resize(numSwapImages); for(uint32_t i=0; i < numSwapImages; i++) { swapInfo.images[i].im = VK_NULL_HANDLE; swapInfo.images[i].view = VK_NULL_HANDLE; swapInfo.images[i].fb = VK_NULL_HANDLE; } VkImage* images = new VkImage[numSwapImages]; // go through our own function so we assign these images IDs ret = vkGetSwapchainImagesKHR(device, *pSwapChain, &numSwapImages, images); RDCASSERTEQUAL(ret, VK_SUCCESS); for(uint32_t i=0; i < numSwapImages; i++) { SwapchainInfo::SwapImage &swapImInfo = swapInfo.images[i]; // memory doesn't exist for genuine WSI created images swapImInfo.im = images[i]; ResourceId imid = GetResID(images[i]); VkImageSubresourceRange range; range.baseMipLevel = range.baseArrayLayer = 0; range.levelCount = 1; range.layerCount = pCreateInfo->imageArrayLayers; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // fill out image info so we track resource state barriers { SCOPED_LOCK(m_ImageLayoutsLock); m_ImageLayouts[imid].subresourceStates.clear(); m_ImageLayouts[imid].subresourceStates.push_back(ImageRegionState(range, UNKNOWN_PREV_IMG_LAYOUT, VK_IMAGE_LAYOUT_UNDEFINED)); } { VkImageViewCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, Unwrap(images[i]), VK_IMAGE_VIEW_TYPE_2D, pCreateInfo->imageFormat, { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, }; vkr = vt->CreateImageView(Unwrap(device), &info, NULL, &swapImInfo.view); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapImInfo.view); VkFramebufferCreateInfo fbinfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, Unwrap(swapInfo.rp), 1, UnwrapPtr(swapImInfo.view), (uint32_t)pCreateInfo->imageExtent.width, (uint32_t)pCreateInfo->imageExtent.height, 1, }; vkr = vt->CreateFramebuffer(Unwrap(device), &fbinfo, NULL, &swapImInfo.fb); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapImInfo.fb); } } SAFE_DELETE_ARRAY(images); } } else { GetResourceManager()->AddLiveResource(id, *pSwapChain); } } return ret; }
void VulkanResourceManager::RecordSingleBarrier(vector<pair<ResourceId, ImageRegionState> > &dststates, ResourceId id, const SrcBarrierType &t, uint32_t nummips, uint32_t numslices) { bool done = false; auto it = dststates.begin(); for(; it != dststates.end(); ++it) { // image barriers are handled by initially inserting one subresource range for each aspect, // and whenever we need more fine-grained detail we split it immediately for one range for // each subresource in that aspect. Thereafter if a barrier comes in that covers multiple // subresources, we update all matching ranges. // find the states matching this id if(it->first < id) continue; if(it->first != id) break; { // we've found a range that completely matches our region, doesn't matter if that's // a whole image and the barrier is the whole image, or it's one subresource. // note that for images with only one array/mip slice (e.g. render targets) we'll never // really have to worry about the else{} branch if(it->second.subresourceRange.baseMipLevel == t.subresourceRange.baseMipLevel && it->second.subresourceRange.levelCount == nummips && it->second.subresourceRange.baseArrayLayer == t.subresourceRange.baseArrayLayer && it->second.subresourceRange.layerCount == numslices) { // verify // RDCASSERT(it->second.newLayout == t.oldLayout); // apply it (prevstate is from the start of all barriers accumulated, so only set once) if(it->second.oldLayout == UNKNOWN_PREV_IMG_LAYOUT) it->second.oldLayout = t.oldLayout; it->second.newLayout = t.newLayout; done = true; break; } else { // this handles the case where the barrier covers a number of subresources and we need // to update each matching subresource. If the barrier was only one mip & array slice // it would have hit the case above. Find each subresource within the range, update it, // and continue (marking as done so whenever we stop finding matching ranges, we are // satisfied. // // note that regardless of how we lay out our subresources (slice-major or mip-major) the // new // range could be sparse, but that's OK as we only break out of the loop once we go past the // whole // aspect. Any subresources that don't match the range, after the split, will fail to meet // any // of the handled cases, so we'll just continue processing. if(it->second.subresourceRange.levelCount == 1 && it->second.subresourceRange.layerCount == 1 && it->second.subresourceRange.baseMipLevel >= t.subresourceRange.baseMipLevel && it->second.subresourceRange.baseMipLevel < t.subresourceRange.baseMipLevel + nummips && it->second.subresourceRange.baseArrayLayer >= t.subresourceRange.baseArrayLayer && it->second.subresourceRange.baseArrayLayer < t.subresourceRange.baseArrayLayer + numslices) { // apply it (prevstate is from the start of all barriers accumulated, so only set once) if(it->second.oldLayout == UNKNOWN_PREV_IMG_LAYOUT) it->second.oldLayout = t.oldLayout; it->second.newLayout = t.newLayout; // continue as there might be more, but we're done done = true; continue; } // finally handle the case where we have a range that covers a whole image but we need to // split it. If the barrier covered the whole image too it would have hit the very first // case, so we know that the barrier doesn't cover the whole range. // Also, if we've already done the split this case won't be hit and we'll either fall into // the case above, or we'll finish as we've covered the whole barrier. else if(it->second.subresourceRange.levelCount > 1 || it->second.subresourceRange.layerCount > 1) { pair<ResourceId, ImageRegionState> existing = *it; // remember where we were in the array, as after this iterators will be // invalidated. size_t offs = it - dststates.begin(); size_t count = it->second.subresourceRange.levelCount * it->second.subresourceRange.layerCount; // only insert count-1 as we want count entries total - one per subresource dststates.insert(it, count - 1, existing); // it now points at the first subresource, but we need to modify the ranges // to be valid it = dststates.begin() + offs; for(size_t i = 0; i < count; i++) { it->second.subresourceRange.levelCount = 1; it->second.subresourceRange.layerCount = 1; // slice-major it->second.subresourceRange.baseArrayLayer = uint32_t(i / existing.second.subresourceRange.levelCount); it->second.subresourceRange.baseMipLevel = uint32_t(i % existing.second.subresourceRange.levelCount); it++; } // reset the iterator to point to the first subresource it = dststates.begin() + offs; // the loop will continue after this point and look at the next subresources // so we need to check to see if the first subresource lies in the range here if(it->second.subresourceRange.baseMipLevel >= t.subresourceRange.baseMipLevel && it->second.subresourceRange.baseMipLevel < t.subresourceRange.baseMipLevel + nummips && it->second.subresourceRange.baseArrayLayer >= t.subresourceRange.baseArrayLayer && it->second.subresourceRange.baseArrayLayer < t.subresourceRange.baseArrayLayer + numslices) { // apply it (prevstate is from the start of all barriers accumulated, so only set once) if(it->second.oldLayout == UNKNOWN_PREV_IMG_LAYOUT) it->second.oldLayout = t.oldLayout; it->second.newLayout = t.newLayout; // continue as there might be more, but we're done done = true; } // continue processing from here continue; } } } // otherwise continue to try and find the subresource range } if(done) return; // we don't have an existing barrier for this memory region, insert into place. it points to // where it should be inserted VkImageSubresourceRange subRange = t.subresourceRange; subRange.levelCount = nummips; subRange.layerCount = numslices; dststates.insert(it, std::make_pair(id, ImageRegionState(subRange, t.oldLayout, t.newLayout))); }