Trying to take a screenshot

I am trying to add the ability to take a screenshot in my renderer, but vkMapMemory keeps producing an error in the debug validation layer, and does not return VK_SUCCESS.

Mapping Memory without VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT. The Vulkan spec states: memory must have been created with a memory type that reports VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT

However, the memory allocation is being created with this flag set in the memory requirements. Here is my code that creates the image to copy the screen data to:

VkAssert(vkCreateImage(device->device, &imageInfo, nullptr, &core->vkimage));
vkGetImageMemoryRequirements(device->device, core->vkimage, &memRequirements[0]);
if ((flags & TEXTURE_LINEARTILING) != 0)
{
	memRequirements[0].memoryTypeBits |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
}
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
VmaAllocationInfo alllocation = {};
VmaAllocationInfo allocinfo = {};
VkAssert(vmaAllocateMemory(UltraEngine::Core::GameEngine::Get()->renderingthreadmanager->device->allocator, &memRequirements[0], &allocCreateInfo, &core->memoryallocation, &allocinfo));
VkAssert(vmaBindImageMemory(this->device->allocator, core->memoryallocation, core->vkimage));

And this is my code that takes the screenshot. There is presently no copy/blit of the framebuffer to the screenshot image. At this point I am just trying to map/unmap the screenshot image memory:

auto pixmap = CreatePixmap(size.x, size.y, TextureFormat(chaininfo.imageFormat));

screencaptureimage = std::make_shared<RenderTexture>();
screencaptureimage->Initialize(TEXTURE_2D, TextureFormat(chaininfo.imageFormat), size.x, size.y, 1, 0, false, 1, TEXTURE_LINEARTILING);

device->WaitIdle();

VmaAllocationInfo meminfo = {};
vmaGetAllocationInfo(device->allocator, screencaptureimage->core->memoryallocation, &meminfo);
			
// Get layout of the image (including row pitch)
const char* data = nullptr;
VkImageSubresource subResource{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
VkSubresourceLayout subResourceLayout;
vkGetImageSubresourceLayout(device->device, screencaptureimage->core->vkimage, &subResource, &subResourceLayout);
			
if (VK_SUCCESS == vkMapMemory(device->device, meminfo.deviceMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data))
{
	data += subResourceLayout.offset;
	Assert(data, subResourceLayout.size == pixmap->pixels->GetSize());
	memcpy(pixmap->pixels->Data(), data, subResourceLayout.size);
	vkUnmapMemory(device->device, meminfo.deviceMemory);
	capturedframes.push_back(pixmap);
}

Any idea what I am doing wrong?

Once I looked at my own post, this line jumped out at me;

allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;

It’s strange how I could not see this in Visual Studio, and yet once I posted it in a forum it became obvious.

I’m also on a laptop right now, so no nice big 28" screen.

I have it working, but there is a problem when I change to another framebuffer / swap chain. The smaller thumbnail gets rendered first, then the larger one. It distinctly looks like the memory of the smaller thumbnail image is being added to the beginning of the larger image’s memory, although I have no idea how this is possible:


My screenshot code is pretty self-explanatory:

	void RenderContext::Screenshot()
	{
		Assert(renderbuffers[currentFrame]->colortexture[0]->size.x == size.x);
		device->WaitIdle();

		auto cb = std::make_shared<GPUCommandBuffer>(device->commandpool[0], VK_COMMAND_BUFFER_LEVEL_PRIMARY, COMMAND_BUFFER_TYPE_DRAW);
		cb->Reset();
		cb->Begin(false);
		if (screencaptureimage)
		{
			if (this->size.x != screencaptureimage->size.x or this->size.y != screencaptureimage->size.y) screencaptureimage = NULL;
		}
		if (screencaptureimage == NULL)
		{
			screencaptureimage = std::make_shared<RenderTexture>();
			screencaptureimage->Initialize(TEXTURE_2D, TextureFormat(chaininfo.imageFormat), size.x, size.y, 1, 0, false, 1, TEXTURE_CAPTURE);
		}
		cb->CopyImage(renderbuffers[currentFrame]->colortexture[0], screencaptureimage);
		cb->TransitionImageLayout(renderbuffers[currentFrame]->colortexture[0], VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
		cb->End();

		VkSubmitInfo submitInfo = {};
		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &cb->commandbuffer;
		submitInfo.waitSemaphoreCount = 0;
		submitInfo.signalSemaphoreCount = 0;
		VkAssert(vkQueueSubmit(device->devicequeue[0], 1, &submitInfo, VK_NULL_HANDLE));

		device->WaitIdle();

		queuedscreencaptures--;

		VmaAllocationInfo meminfo = {};
		vmaGetAllocationInfo(device->allocator, screencaptureimage->core->memoryallocation, &meminfo);

		// Get layout of the image (including row pitch)
		const char* data = nullptr;
		VkImageSubresource subResource{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
		VkSubresourceLayout subResourceLayout;
		vkGetImageSubresourceLayout(device->device, screencaptureimage->core->vkimage, &subResource, &subResourceLayout);

		Assert(screencaptureimage->size.x == this->size.x and screencaptureimage->size.y == this->size.y);

		//Copy image
		if (VK_SUCCESS == vkMapMemory(device->device, meminfo.deviceMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data))
		{
			auto pixmap = CreatePixmap(size.x, size.y, TextureFormat(chaininfo.imageFormat));
			data += subResourceLayout.offset;
			auto sz = pixmap->pixels->GetSize();
			Assert(subResourceLayout.size - subResourceLayout.offset >= sz);
			char* dst = pixmap->pixels->Data();
			uint64_t pitch = uint64_t(pixmap->size.x * Core::GetFormatBpp(pixmap->format));
			//if (pitch == subResourceLayout.rowPitch)
			//{
			//	memcpy(dst, data, Min(subResourceLayout.size - subResourceLayout.offset, pixmap->pixels->GetSize()));
			//}
			//else
			//{
			for (int y = 0; y < pixmap->size.y; ++y)
			{
				memcpy(dst, data, pitch);
				data += subResourceLayout.rowPitch;
				dst += pitch;
			}
			vkUnmapMemory(device->device, meminfo.deviceMemory);
			capturedframes.push_back(pixmap);
			//pixmap->Save(GetPath(PATH_DESKTOP) + "/test.dds");
		}
	}

I have never seen anything like this. Any ideas what is going on here?

If I do one render at 80x80, and then a second at 160x160, the two screenshots look like this:

First screenshot:
80

Second screenshot:
160

If I don’t render the first image, then the image at 160x160 resolution looks as you would expect:
160

This doesn’t look like anything I’ve ever seen. There’s clearly a logic to it, like some kind of memory offset.

The renders, however, appears correctly when I make the windows visible.
Image1

Those are the exact images that are being copied and their memory mapped. I’m only rendering one frame for each window.

What is going on?

Who’s the best programmer of all time?

<— This guy. :crazy_face:

vkMapMemory(device->device, meminfo.deviceMemory, meminfo.offset, VK_WHOLE_SIZE, 0, (void**)&data))

The mem offset was missing.

You are not the best I did better> vkCopy giving the samebuffer copy from same offset and same size

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.