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 2 months, 1 week ago

I got the Intel Open Image Denoiser library working in conjunction with the high-res screenshot functionality. With regard to the image attached below, on the left is the denoised image, and on the right the noisy input image. Works pretty good!

I do not have it working in conjunction with the render to screen functionality however.

I basically need to trace the rays, then read that into a CPU buffer, run it through the denoiser, and then write that back into GPU memory to be drawn to the screen.

I have no idea where to begin. I am using Sascha Willems' raytracingreflections demo code as a base.

https://github.com/sjhalayka/bidirectional_path_tracer/blob/a7cdc885fc850cf6555f56c320bfdeed240c409c/raytracingreflections.cpp#L1081

Any Vulkan experts in the crowd? :)

\

Advertisement

I'm trying to copy from a vector<unsigned char> back to the screenshot image. Any glaring errors in my code? Am I going about this all wrong?

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

vks::tools::setImageLayout(
	screenshotCmdBuffer,
	screenshotStorageImage.image,
	VK_IMAGE_LAYOUT_GENERAL,
	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 = px;
copyRegion.imageExtent.height = py;
copyRegion.imageExtent.depth = 1;

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

VkBufferMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcAccessMask = VK_ACCESS_HOST_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.buffer = screenshotStagingBuffer.buffer;
barrier.size = screenshotStagingBuffer.size;

// Crashes here!
vkCmdPipelineBarrier(
	screenshotCmdBuffer,
	VK_PIPELINE_STAGE_HOST_BIT,
	VK_PIPELINE_STAGE_TRANSFER_BIT,
	0,
	0, nullptr,
	1, &barrier,
	0, nullptr);

vulkanDevice->flushCommandBuffer(screenshotCmdBuffer, queue);

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

taby said:
// Crashes here!

Do you use Validation Layers to get verbose error descriptions?

JoeJ said:

taby said:
// Crashes here!

Do you use Validation Layers to get verbose error descriptions?

I do not, because I'm not familiar with them. Let me look into it. Thanks for the pointer!

I've gone through a few tutorials on enabling validation layers, but it's not clear to me how to enable them.

I wonder why I can't just do this and be done with it:

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

taby said:
I've gone through a few tutorials on enabling validation layers, but it's not clear to me how to enable them.

It's really a must do. It's a debug callback. When there is an error, in the callback you get pointers to involved VK objects and a text message explaining the error. It's then easy to track back and fix things.

Posting some code to enable it, which i've got from some minimal ‘render a rotating cube’ example that came with the Vulkan SDK.

It won't help you much, but at least it gives some functions to search for.

I know this whole setup should be much easier nowadays. See the usual VK tutorials. I'm sure Sascha Willems has this too. Nobody could start with VK without this help.

	static VKAPI_ATTR VkBool32 VKAPI_CALL
	BreakCallback (VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
				  uint64_t srcObject, size_t location, int32_t msgCode,
				  const char *pLayerPrefix, const char *pMsg,
				  void *pUserData) 
	{
		SystemTools::Log (pMsg); // the text massage describing the error
		SystemTools::Log ("\n");


		// some messages i have disabled manually, which i got after uninmstalling Steam or NVidia GPU...

		
		//if (!strcmp(pMsg, " [ UNASSIGNED-CoreValidation-Shader-DescriptorTypeMismatch ] Object: VK_NULL_HANDLE (Type = 0) | Type mismatch on descriptor slot 0.0 (expected `VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC`) but descriptor of type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER")) return false;
		
		if (!strcmp(pMsg, "loader_get_json: Failed to open JSON file C:\\Program Files (x86)\\Steam\\SteamFossilizeVulkanLayer64.json")) return false; // uninstalling Steam? how dare you !!?
		if (!strcmp(pMsg, "loader_get_json: Failed to open JSON file C:\\Program Files (x86)\\Steam\\SteamOverlayVulkanLayer64.json")) return false;


		DebugBreak(); // Win32 function acting like a breakpoint
		return false;
	}
	
	
	




	struct Context
	{
		enum 
		{
			MAX_GPUS = 8,
		};

		VkInstance inst; // Vulkan instance, stores all per-application states

		struct Gpu gpus[MAX_GPUS];
		uint32_t gpuCount;

		bool validate;
		bool use_break;
		
		PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback;
		PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback;
		VkDebugReportCallbackEXT msg_callback;
		PFN_vkDebugReportMessageEXT DebugReportMessage;

		void InitVK (const char* APP_SHORT_NAME, const bool validate = false, const bool use_break = false);
		
		void Destroy ()
		{
			for (uint32_t i=0; i<gpuCount; i++) gpus[i].Destroy();
			if (validate) DestroyDebugReportCallback (inst, msg_callback, NULL);
			vkDestroyInstance (inst, NULL);
		}
	};


	void Context::InitVK (const char* APP_SHORT_NAME, const bool validate, const bool use_break)
	{
		SystemTools::Log ("InitVK...\n");

		this->validate = validate;
		this->use_break = use_break;

		for (int i=0; i<MAX_GPUS; i++) gpus[i].Init();
		gpuCount = 0;

			
		uint32_t instance_validation_layer_count = 0;
		//@char *instance_validation_layers[MAX_LAYERS] = {};
		const char *instance_validation_layers[] = {""};//"VK_LAYER_LUNARG_standard_validation"};
			
		uint32_t enabled_instance_extension_count = 0;
		char const *instance_extension_names[MAX_EXTENSIONS] = {};

		VkResult err;



		// Look for instance layers 
		if (validate) 
		{
			//@VkBool32 validation_found = FindInstanceValidationLayers (instance_validation_layers, instance_validation_layer_count);
			instance_validation_layer_count = 0;//1;
		}	

		// Look for instance extensions 
		VkExtensionProperties* instance_extensions = NULL; 
		FindInstanceExtensions (instance_extensions, 
			enabled_instance_extension_count, instance_extension_names, 	
			MAX_EXTENSIONS, validate, true);

		inst = CreateInstance (APP_SHORT_NAME, 
			instance_validation_layer_count,
			instance_validation_layers,
			enabled_instance_extension_count,
			instance_extension_names,
			validate, use_break);

		if(instance_extensions) free(instance_extensions);



		// Make initial call to query gpu_count, then second call for physicalDevice info
		err = vkEnumeratePhysicalDevices(inst, &gpuCount, NULL);
		assert(!err && gpuCount > 0);

		SystemTools::Log ("\nfound GPUs: %i\n", gpuCount);

		if (gpuCount > 0) 
		{
			VkPhysicalDevice *physical_devices = (VkPhysicalDevice*) malloc(sizeof(VkPhysicalDevice) * gpuCount);
			err = vkEnumeratePhysicalDevices(inst, &gpuCount, physical_devices);
			assert(!err);

			for (uint32_t i=0; i<min(MAX_GPUS, gpuCount); i++)
			{
				gpus[i].physicalDevice = physical_devices[i]; //@@@
				vkGetPhysicalDeviceProperties(gpus[i].physicalDevice, &gpus[i].physicalDeviceProps);
				vkGetPhysicalDeviceFeatures(gpus[i].physicalDevice, &gpus[i].features);
				vkGetPhysicalDeviceMemoryProperties(gpus[i].physicalDevice, &gpus[i].memoryProperties);

				SystemTools::Log ("deviceName: %s\n", gpus[i].physicalDeviceProps.deviceName);
				SystemTools::Log ("apiVersion: %i\n", gpus[i].physicalDeviceProps.apiVersion);
				SystemTools::Log ("driverVersion: %i\n", gpus[i].physicalDeviceProps.driverVersion);
#if 0
				// Look for validation layers
				if (validate) 
				{
						uint32_t device_enabled_layer_count;
						VkBool32 validation_found = CheckDeviceValidationLayers (device_enabled_layer_count,
							gpus[i].physicalDevice, instance_validation_layer_count, instance_validation_layers, validate);
				}
#endif

				// Look for device extensions

				uint32_t enabled_device_extension_count = 0;
				char *device_extension_names[MAX_EXTENSIONS] = {};

				VkExtensionProperties* device_extensions = NULL;

				LoadDeviceExtensions (device_extensions, enabled_device_extension_count, device_extension_names, 	
					gpus[i].physicalDevice, MAX_EXTENSIONS, true, /*true*/false); // todo: loading all Extensions causes vkCreateDevice to fail on AMD
			



				gpus[i].device = CreateDevice (
					gpus[i].queueFamilyProps,	
					gpus[i].queueFamilyCount,
					gpus[i].physicalDevice,
					instance_validation_layer_count,
					(const char *const *)((validate) ? instance_validation_layers : NULL),
					enabled_device_extension_count,
					device_extension_names);

				if (device_extensions) free(device_extensions); // should we keep them?

				/*VkDevice device2 = 0;
				device2 = CreateDevice (
					gpus[i].queueFamilyProps,	
					gpus[i].queueFamilyCount,
					gpus[i].physicalDevice,
					instance_validation_layer_count,
					(const char *const *)((validate) ? instance_validation_layers : NULL),
					enabled_extension_count,
					extension_names);*/

				VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};
				pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
				err = vkCreatePipelineCache (gpus[i].device, &pipelineCacheCreateInfo, nullptr, &gpus[i].pipelineCache);
				assert(!err);

				SystemTools::Log ("\n");

			}
			free(physical_devices);
		} 
		else 
		{
			ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
						"Do you have a compatible Vulkan installable client driver (ICD) "
						"installed?\nPlease look at the Getting Started guide for "
						"additional information.\n",
						"vkEnumeratePhysicalDevices Failure");
		}

//*
		if (validate) 
		{
			CreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT");
			if (!CreateDebugReportCallback) ERR_EXIT("GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n", "vkGetProcAddr Failure");
			DestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT");
			if (!DestroyDebugReportCallback) ERR_EXIT("GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n", "vkGetProcAddr Failure");
			DebugReportMessage = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT");
			if (!DebugReportMessage) ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n", "vkGetProcAddr Failure");
				
			PFN_vkDebugReportCallbackEXT callback = use_break ? BreakCallback : dbgFunc;
			VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {};
				dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
				dbgCreateInfo.pNext = NULL;
				dbgCreateInfo.pfnCallback = callback;
				dbgCreateInfo.pUserData = NULL;
				dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
			err = CreateDebugReportCallback(inst, &dbgCreateInfo, NULL, &msg_callback);
			switch (err) 
			{
			case VK_SUCCESS:
				break;
			case VK_ERROR_OUT_OF_HOST_MEMORY:
				ERR_EXIT("CreateDebugReportCallback: out of host memory\n", "CreateDebugReportCallback Failure");
				break;
			default:
				ERR_EXIT("CreateDebugReportCallback: unknown failure\n", "CreateDebugReportCallback Failure");
				break;
			}
		}
//*/	


	}

taby said:

I wonder why I can't just do this and be done with it:

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

Maybe you can. I think VK supports some form of pointers to GPU memory now. Was added recently, but maybe that's more about using pointers within SpirV shaders.

Another related issue is the need to unswizzle framebuffer or texture data so the memory layout is as expected, and some compressed format the GPU is using internally. So maybe there is a need for a resource transition i guess.

You will figure it out… : )

Yeah, for some reason I have to swap the R and B channels for onscreen rendering. I just changed the format to BGR.

Thanks for your vote of confidence. :)

JoeJ said:

taby said:
I've gone through a few tutorials on enabling validation layers, but it's not clear to me how to enable them.

It's really a must do. It's a debug callback. When there is an error, in the callback you get pointers to involved VK objects and a text message explaining the error. It's then easy to track back and fix things.

Posting some code to enable it, which i've got from some minimal ‘render a rotating cube’ example that came with the Vulkan SDK.

It won't help you much, but at least it gives some functions to search for.

I know this whole setup should be much easier nowadays. See the usual VK tutorials. I'm sure Sascha Willems has this too. Nobody could start with VK without this help.

Thank you for showing me the way. I will read through your code.

This topic is closed to new replies.

Advertisement