Reading ray tracing result to the CPU and back onto the GPU in order to do image denoising

Started by
109 comments, last by JoeJ 3 months ago

taby said:
I will read through your code.

It's a mess. There should be better examples to find.

I still have problems with minimizing my window and making it big again, which rarely causes a crash (and some validation warning regarding swap chain which i've ignored so far).

I'm migrating to Linux currently, and i'll try to use GLFW to abstract OS.
Ideally this would also include VK initialization and validation setup, but probably i'm expecting too much, considering it's primarily for OpenGL.

An app framework library written specifically for VK would be really nice.

Advertisement

It's no messier than the other examples that I read through.

Regarding the path tracing: do I need to use a staging buffer to copy from CPU memory to image? Or can I somehow just use memcpy? You'd think it would be well documented, especially on stack exchange.

taby said:
do I need to use a staging buffer to copy from CPU memory to image? Or can I somehow just use memcpy? You'd think it would be well documented, especially on stack exchange.

The problem is that docs read like patents, and tutorials etc. can't get around that slang easily either.

I helps me to know about the HW. From that i make an assumption of what should work deally technically, and then i research resources to confirm those assumptions and providing details on how to implement them.

So in this is case i start from the example of a texture in VRAM. I know very little about framebuffers, but i guess it's similar enough to a texture.

The texture must be tiled. Otherwise for a filtered texel fetch, we would have a large vertical stride between rows of pixels if they span the whole horizontal resolution of the image.
So let's guess they tile it to 16x16 texel blocks, and arrange the tiles in Morton or Hilbert order in memory, or some other cache efficient space filling curve.

Details don't matter, but they surely do something like that. Thus, no matter if our VRAM is on dGPU or iGPU, resource transitions are needed to convert forth and back to those vendor specific formats ('swizzling').
Likely the driver has to dispatch compute shaders to do this work, beside handling the memory transfer itself.

Another point is the type of memory we want to use. Ideal choices depend on HW, so vendors but mainly if it's dGPU or iGPU.
Newer HW can address the whole VRAM on a dGPU afaik, and maybe this could avoid a need for a staging buffer. Idk.
But you'd need a fallback using the staging buffer for older HW anyway, so i would not bother and just use the staging buffer.

Maybe, if you really know what you do, you could use memcpy using all hacks and tricks, on some HW configurations.
But i would not want to do this, because we can't treat all those various memory pools as general RAM. Sometimes the memory can't be cached for example, and so i rather use API functions over memcopy, assuming they implement related care, optimizations, scheduling AGP transfers, etc. properly.

Having all this in mind, i expect related VK functions require information about all those points, and translating patents blah to english becomes a bit easier… : /

Yeah, ‘check the spec' is a common fallback.

OK, the good news is that Validation Layers have been enabled in Sascha WIllems' code:

  1. I added the statement #define _VALIDATION 1 to the top of base\vulkanexamplebase.cpp
  2. I changed base\VulkanDebug.cpp to use a MessageBox (on Windows) or text file (on all systems), instead of cerr and cout. This is because anything written to cerr and cout in Windows are lost to the bit bucket, never to be shown in the terminal window. They just… vanish.

The bad news is that there are hundreds of errors. I've only written like 10% of the C++ code, so I can't be the author of this many fuck ups. LOL – seriously, where does one begin?

I took the chance of getting banned from computer graphics stack exchange by asking this question, basically – how do I copy from a vector of unsigned char to an image? Hopefully they don't ban me!!

taby said:
LOL – seriously, where does one begin?

Well, the first one… :D
… hoping fixing one fixes many others as well… : )

taby said:
how do I copy from a vector of unsigned char to an image?

You mean you have not yet uploaded textures at all?

I could post related code, but probably it's a lot.

EDIT:

Actually i remember Willems code has related examples of using the staging buffer to upload textures.
You should be able to copy paste from that i guess.

No sir, I am a rank amateur when it comes to the C++ end of Vulkan.

OK, I've got the validation to a manageable state. There are like 10 errors, not hundreds anymore. Whew!


ERROR: [1458672316][VUID-VkSamplerCreateInfo-anisotropyEnable-01070] : Validation Error: [ VUID-VkSamplerCreateInfo-anisotropyEnable-01070 ] | MessageID = 0x56f192bc | vkCreateSampler(): Anisotropic sampling feature is not enabled, pCreateInfo->anisotropyEnable must be VK_FALSE. The Vulkan spec states: If the samplerAnisotropy feature is not enabled, anisotropyEnable must be VK_FALSE (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-VkSamplerCreateInfo-anisotropyEnable-01070)

ERROR: [1458672316][VUID-VkSamplerCreateInfo-anisotropyEnable-01070] : Validation Error: [ VUID-VkSamplerCreateInfo-anisotropyEnable-01070 ] | MessageID = 0x56f192bc | vkCreateSampler(): Anisotropic sampling feature is not enabled, pCreateInfo->anisotropyEnable must be VK_FALSE. The Vulkan spec states: If the samplerAnisotropy feature is not enabled, anisotropyEnable must be VK_FALSE (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-VkSamplerCreateInfo-anisotropyEnable-01070)

ERROR: [706474367][VUID-VkShaderModuleCreateInfo-pCode-01379] : Validation Error: [ VUID-VkShaderModuleCreateInfo-pCode-01379 ] | MessageID = 0x2a1bf17f | SPIR-V module not valid: Invalid SPIR-V binary version 1.5 for target environment SPIR-V 1.4 (under Vulkan 1.1 semantics). The Vulkan spec states: If pCode is a pointer to GLSL code, it must be valid GLSL code written to the GL_KHR_vulkan_glsl GLSL extension specification (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-VkShaderModuleCreateInfo-pCode-01379)

ERROR: [706474367][VUID-VkShaderModuleCreateInfo-pCode-01379] : Validation Error: [ VUID-VkShaderModuleCreateInfo-pCode-01379 ] | MessageID = 0x2a1bf17f | SPIR-V module not valid: Invalid SPIR-V binary version 1.5 for target environment SPIR-V 1.4 (under Vulkan 1.1 semantics). The Vulkan spec states: If pCode is a pointer to GLSL code, it must be valid GLSL code written to the GL_KHR_vulkan_glsl GLSL extension specification (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-VkShaderModuleCreateInfo-pCode-01379)

ERROR: [706474367][VUID-VkShaderModuleCreateInfo-pCode-01379] : Validation Error: [ VUID-VkShaderModuleCreateInfo-pCode-01379 ] | MessageID = 0x2a1bf17f | SPIR-V module not valid: Invalid SPIR-V binary version 1.5 for target environment SPIR-V 1.4 (under Vulkan 1.1 semantics). The Vulkan spec states: If pCode is a pointer to GLSL code, it must be valid GLSL code written to the GL_KHR_vulkan_glsl GLSL extension specification (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-VkShaderModuleCreateInfo-pCode-01379)

ERROR: [706474367][VUID-VkShaderModuleCreateInfo-pCode-01379] : Validation Error: [ VUID-VkShaderModuleCreateInfo-pCode-01379 ] | MessageID = 0x2a1bf17f | SPIR-V module not valid: Invalid SPIR-V binary version 1.5 for target environment SPIR-V 1.4 (under Vulkan 1.1 semantics). The Vulkan spec states: If pCode is a pointer to GLSL code, it must be valid GLSL code written to the GL_KHR_vulkan_glsl GLSL extension specification (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-VkShaderModuleCreateInfo-pCode-01379)

ERROR: [1132206547][VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849] : Validation Error: [ VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849 ] | MessageID = 0x437c19d3 | vkCreateRayTracingPipelinesKHR(): pCreateInfos[0] After specialization was applied, VkShaderModule 0x5011aa0000000066[] does not contain valid spirv for stage VK_SHADER_STAGE_RAYGEN_BIT_KHR. The Vulkan spec states: If a shader module identifier is not specified, the shader code used by the pipeline must be valid as described by the Khronos SPIR-V Specification after applying the specializations provided in pSpecializationInfo, if any, and then converting all specialization constants into fixed constants (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849)

ERROR: [-507995293][VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358] : Validation Error: [ VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358 ] Object 0: handle = 0x612f93000000004e, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xe1b89b63 | vkCmdBindDescriptorSets(): descriptorSet #1 being bound is not compatible with overlapping descriptorSetLayout at index 1 of VkPipelineLayout 0x5dbcf90000000065[] due to: Binding 0 for VkDescriptorSetLayout 0x9f58380000000064[] from pipeline layout has stageFlags VK_SHADER_STAGE_RAYGEN_BIT_KHR|VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR but binding 0 for VkDescriptorSetLayout 0x73a850000000004d[], which is bound, has stageFlags VK_SHADER_STAGE_FRAGMENT_BIT. The Vulkan spec states: Each element of pDescriptorSets must have been allocated with a VkDescriptorSetLayout that matches (is the same as, or identically defined as) the VkDescriptorSetLayout at set n in layout, where n is the sum of firstSet and the index into pDescriptorSets (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358)

ERROR: [387543851][VUID-vkCmdCopyImageToBuffer-srcImageLayout-00189] : Validation Error: [ VUID-vkCmdCopyImageToBuffer-srcImageLayout-00189 ] Object 0: handle = 0x22a6263b320, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x421a0f0000000074, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x1719732b | vkCmdCopyImageToBuffer: Cannot use VkImage 0x421a0f0000000074[] (layer=0 mip=0) with specific layout VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL that doesn't match the previous known layout VK_IMAGE_LAYOUT_GENERAL. The Vulkan spec states: srcImageLayout must specify the layout of the image subresources of srcImage specified in pRegions at the time this command is executed on a VkDevice (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-vkCmdCopyImageToBuffer-srcImageLayout-00189)

ERROR: [455308668][VUID-vkCmdPipelineBarrier-commandBuffer-parameter] : Validation Error: [ VUID-vkCmdPipelineBarrier-commandBuffer-parameter ] Object 0: handle = 0x22a5165bde0, type = VK_OBJECT_TYPE_INSTANCE; | MessageID = 0x1b23757c | vkCmdPipelineBarrier(): Invalid VkCommandBuffer Object 0x22a6263b320. The Vulkan spec states: commandBuffer must be a valid VkCommandBuffer handle (https://vulkan.lunarg.com/doc/view/1.3.261.1/windows/1.3-extensions/vkspec.html#VUID-vkCmdPipelineBarrier-commandBuffer-parameter)

I'll have to meditate upon these errrors.

taby said:
I'll have to meditate upon these errrors.

Nice and informative error messages. They really work on making it as easy to use as possible.
They also send surveys asking for feedback, and work on some ‘easy’ paths.

People often rant about the Khronos committee, but i think they do good work.

The error about the image layout could be related.

taby said:
I am a rank amateur when it comes to the C++ end of Vulkan.

I can imagine you just use Willems RT example and focused on the things which interest you.

But his code is educational. You can look up other examples to see how this or that works.
He also has some abstractions and tooling functions, and you can use them.
I also have them. They work well at the lowest level, e.g. to ease up work with memory buffers, command buffers, and uploading them.

I would look for his simplest example to use some texture, and copy paste from there.
Validation messages help to guide you from there. But even the simplest things are and remain difficult.

Some oversight is needed ofc. E.g. keep in mind that anything the GPU should do has to be recorded to a command buffer, uploaded, and executed. That's different from calling an API function which might do some work on CPU right away, and sometimes this has confused me initially.

Yes, i miss those immediate mode glVertex() commands too! :D

I still use glVertex calls in my simple apps. :)

Yes, I stole Sascha Willems' code and used his path tracer as a base. So, I wrote like 90% of the shader code.

Thanks again for your input JoeJ.

OK, I figured it out. I was not recreating the command buffer after it's been flushed. This is basically what validation layers were saying. Thanks for the guidance with validation layers.

The working code uses a image memory barrier:


		if (num_cams_wide == 1)
		{
			VkCommandBuffer screenshotCmdBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);

			memcpy(screenshotStagingBuffer.mapped, &uc_output_data[0], size);

			VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };

			vks::tools::setImageLayout(
				screenshotCmdBuffer,
				screenshotStorageImage.image,
				VK_IMAGE_LAYOUT_UNDEFINED,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				subresourceRange);

			VkBufferImageCopy copyRegion{};
			copyRegion.bufferOffset = 0;
			copyRegion.bufferRowLength = 0;
			copyRegion.bufferImageHeight = 0;
			copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			copyRegion.imageSubresource.mipLevel = 0;
			copyRegion.imageSubresource.baseArrayLayer = 0;
			copyRegion.imageSubresource.layerCount = 1;
			copyRegion.imageOffset = { 0, 0, 0 };
			copyRegion.imageExtent.width = size_x;
			copyRegion.imageExtent.height = size_y;
			copyRegion.imageExtent.depth = 1;

			VkImageMemoryBarrier imageMemoryBarrier;
			imageMemoryBarrier.image = screenshotStorageImage.image;
			imageMemoryBarrier.subresourceRange = subresourceRange;
			imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_READ_BIT;
			imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
			imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
			imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;

			vkCmdCopyBufferToImage(
				screenshotCmdBuffer,
				screenshotStagingBuffer.buffer,
				screenshotStorageImage.image,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				1,
				&copyRegion);

			vkCmdPipelineBarrier(
				screenshotCmdBuffer,
				VK_PIPELINE_STAGE_HOST_BIT,
				VK_PIPELINE_STAGE_TRANSFER_BIT,
				0,
				0, nullptr,
				0, nullptr,
				1, &imageMemoryBarrier);

			vulkanDevice->flushCommandBuffer(screenshotCmdBuffer, queue);
		}

The final image is the denoised image:

This topic is closed to new replies.

Advertisement