Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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)));
}