yum-mirror/VkFFT

git clone https://git.yummers.dev/yum-mirror/VkFFT

Tolmachev DmitriiVkFFT v1.3.2 release3fa0c21

master
45.7 KiB1003 linesraw
1//general parts
2#include <stdio.h>
3#include <vector>
4#include <memory>
5#include <string.h>
6#include <chrono>
7#include <thread>
8#include <iostream>
9#ifndef __STDC_FORMAT_MACROS
10#define __STDC_FORMAT_MACROS
11#endif
12#include <inttypes.h>
13
14#if(VKFFT_BACKEND==0)
15#include "vulkan/vulkan.h"
16#include "glslang_c_interface.h"
17#elif(VKFFT_BACKEND==1)
18#include <cuda.h>
19#include <cuda_runtime.h>
20#include <nvrtc.h>
21#include <cuda_runtime_api.h>
22#include <cuComplex.h>
23#elif(VKFFT_BACKEND==2)
24#ifndef __HIP_PLATFORM_HCC__
25#define __HIP_PLATFORM_HCC__
26#endif
27#include <hip/hip_runtime.h>
28#include <hip/hiprtc.h>
29#include <hip/hip_runtime_api.h>
30#include <hip/hip_complex.h>
31#elif(VKFFT_BACKEND==3)
32#ifndef CL_USE_DEPRECATED_OPENCL_1_2_APIS
33#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
34#endif
35#ifdef __APPLE__
36#include <OpenCL/opencl.h>
37#else
38#include <CL/cl.h>
39#endif 
40#elif(VKFFT_BACKEND==4)
41#include <ze_api.h>
42#elif(VKFFT_BACKEND==5)
43#include "Foundation/Foundation.hpp"
44#include "QuartzCore/QuartzCore.hpp"
45#include "Metal/Metal.hpp"
46#endif
47#include "vkFFT.h"
48#include "utils_VkFFT.h"
49#if(VKFFT_BACKEND==0)
50
51VkResult CreateDebugUtilsMessengerEXT(VkGPU* vkGPU, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
52	//pointer to the function, as it is not part of the core. Function creates debugging messenger
53	PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vkGPU->instance, "vkCreateDebugUtilsMessengerEXT");
54	if (func != NULL) {
55		return func(vkGPU->instance, pCreateInfo, pAllocator, pDebugMessenger);
56	}
57	else {
58		return VK_ERROR_EXTENSION_NOT_PRESENT;
59	}
60}
61void DestroyDebugUtilsMessengerEXT(VkGPU* vkGPU, const VkAllocationCallbacks* pAllocator) {
62	//pointer to the function, as it is not part of the core. Function destroys debugging messenger
63	PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vkGPU->instance, "vkDestroyDebugUtilsMessengerEXT");
64	if (func != NULL) {
65		func(vkGPU->instance, vkGPU->debugMessenger, pAllocator);
66	}
67}
68static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
69	printf("validation layer: %s\n", pCallbackData->pMessage);
70	return VK_FALSE;
71}
72
73
74VkResult setupDebugMessenger(VkGPU* vkGPU) {
75	//function that sets up the debugging messenger 
76	if (vkGPU->enableValidationLayers == 0) return VK_SUCCESS;
77
78	VkDebugUtilsMessengerCreateInfoEXT createInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };
79	createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
80	createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
81	createInfo.pfnUserCallback = debugCallback;
82
83	if (CreateDebugUtilsMessengerEXT(vkGPU, &createInfo, NULL, &vkGPU->debugMessenger) != VK_SUCCESS) {
84		return VK_ERROR_INITIALIZATION_FAILED;
85	}
86	return VK_SUCCESS;
87}
88VkResult checkValidationLayerSupport() {
89	//check if validation layers are supported when an instance is created
90	uint32_t layerCount;
91	vkEnumerateInstanceLayerProperties(&layerCount, NULL);
92
93	VkLayerProperties* availableLayers = (VkLayerProperties*)malloc(sizeof(VkLayerProperties) * layerCount);
94	if (!availableLayers) return VK_INCOMPLETE;
95	vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
96	if (availableLayers) {
97		for (uint64_t i = 0; i < layerCount; i++) {
98			if (strcmp("VK_LAYER_KHRONOS_validation", availableLayers[i].layerName) == 0) {
99				free(availableLayers);
100				return VK_SUCCESS;
101			}
102		}
103		free(availableLayers);
104	}
105	else {
106		return VK_INCOMPLETE;
107	}
108	return VK_ERROR_LAYER_NOT_PRESENT;
109}
110
111std::vector<const char*> getRequiredExtensions(VkGPU* vkGPU, uint64_t sample_id) {
112	std::vector<const char*> extensions;
113
114	if (vkGPU->enableValidationLayers) {
115		extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
116	}
117	switch (sample_id) {
118#if (VK_API_VERSION>10)
119	case 2: case 102:
120		extensions.push_back("VK_KHR_get_physical_device_properties2");
121		break;
122#endif
123	default:
124		break;
125	}
126
127
128	return extensions;
129}
130
131VkResult createInstance(VkGPU* vkGPU, uint64_t sample_id) {
132	//create instance - a connection between the application and the Vulkan library 
133	VkResult res = VK_SUCCESS;
134	//check if validation layers are supported
135	if (vkGPU->enableValidationLayers == 1) {
136		res = checkValidationLayerSupport();
137		if (res != VK_SUCCESS) return res;
138	}
139
140	VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
141	applicationInfo.pApplicationName = "VkFFT";
142	applicationInfo.applicationVersion = (uint32_t)VkFFTGetVersion();
143	applicationInfo.pEngineName = "VkFFT";
144	applicationInfo.engineVersion = 1;
145#if (VK_API_VERSION>=12)
146	applicationInfo.apiVersion = VK_API_VERSION_1_2;
147#elif (VK_API_VERSION==11)
148	applicationInfo.apiVersion = VK_API_VERSION_1_1;
149#else
150	applicationInfo.apiVersion = VK_API_VERSION_1_0;
151#endif
152
153	VkInstanceCreateInfo createInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
154	createInfo.flags = 0;
155	createInfo.pApplicationInfo = &applicationInfo;
156
157	auto extensions = getRequiredExtensions(vkGPU, sample_id);
158	createInfo.enabledExtensionCount = (uint32_t)(extensions.size());
159	createInfo.ppEnabledExtensionNames = extensions.data();
160
161	VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };
162	if (vkGPU->enableValidationLayers) {
163		//query for the validation layer support in the instance
164		createInfo.enabledLayerCount = 1;
165		const char* validationLayers = "VK_LAYER_KHRONOS_validation";
166		createInfo.ppEnabledLayerNames = &validationLayers;
167		debugCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
168		debugCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
169		debugCreateInfo.pfnUserCallback = debugCallback;
170		createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
171	}
172	else {
173		createInfo.enabledLayerCount = 0;
174
175		createInfo.pNext = nullptr;
176	}
177
178	res = vkCreateInstance(&createInfo, NULL, &vkGPU->instance);
179	if (res != VK_SUCCESS) return res;
180
181	return res;
182}
183
184VkResult findPhysicalDevice(VkGPU* vkGPU) {
185	//check if there are GPUs that support Vulkan and select one
186	VkResult res = VK_SUCCESS;
187	uint32_t deviceCount;
188	res = vkEnumeratePhysicalDevices(vkGPU->instance, &deviceCount, NULL);
189	if (res != VK_SUCCESS) return res;
190	if (deviceCount == 0) {
191		return VK_ERROR_DEVICE_LOST;
192	}
193
194	VkPhysicalDevice* devices = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * deviceCount);
195	if (!devices) return VK_INCOMPLETE;
196	res = vkEnumeratePhysicalDevices(vkGPU->instance, &deviceCount, devices);
197	if (res != VK_SUCCESS) return res;
198	if (devices) {
199		vkGPU->physicalDevice = devices[vkGPU->device_id];
200		free(devices);
201		return VK_SUCCESS;
202	}
203	else
204		return VK_INCOMPLETE;
205}
206VkResult getComputeQueueFamilyIndex(VkGPU* vkGPU) {
207	//find a queue family for a selected GPU, select the first available for use
208	uint32_t queueFamilyCount;
209	vkGetPhysicalDeviceQueueFamilyProperties(vkGPU->physicalDevice, &queueFamilyCount, NULL);
210
211	VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * queueFamilyCount);
212	if (!queueFamilies) return VK_INCOMPLETE;
213	if (queueFamilies) {
214		vkGetPhysicalDeviceQueueFamilyProperties(vkGPU->physicalDevice, &queueFamilyCount, queueFamilies);
215		uint64_t i = 0;
216		for (; i < queueFamilyCount; i++) {
217			VkQueueFamilyProperties props = queueFamilies[i];
218
219			if (props.queueCount > 0 && (props.queueFlags & VK_QUEUE_COMPUTE_BIT)) {
220				break;
221			}
222		}
223		free(queueFamilies);
224		if (i == queueFamilyCount) {
225			return VK_ERROR_INITIALIZATION_FAILED;
226		}
227		vkGPU->queueFamilyIndex = i;
228		return VK_SUCCESS;
229	}
230	else
231		return VK_INCOMPLETE;
232}
233
234VkResult createDevice(VkGPU* vkGPU, uint64_t sample_id) {
235	//create logical device representation
236	VkResult res = VK_SUCCESS;
237	VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
238	res = getComputeQueueFamilyIndex(vkGPU);
239	if (res != VK_SUCCESS) return res;
240	queueCreateInfo.queueFamilyIndex = (uint32_t)vkGPU->queueFamilyIndex;
241	queueCreateInfo.queueCount = 1;
242	float queuePriorities = 1.0;
243	queueCreateInfo.pQueuePriorities = &queuePriorities;
244	VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
245	VkPhysicalDeviceFeatures deviceFeatures = {};
246	switch (sample_id) {
247	case 1: case 9: case 12: case 17: case 18: case 19: case 101: case 201: case 203: case 1001: case 1004: {
248		deviceFeatures.shaderFloat64 = true;
249		deviceCreateInfo.enabledExtensionCount = (uint32_t)vkGPU->enabledDeviceExtensions.size();
250		deviceCreateInfo.ppEnabledExtensionNames = vkGPU->enabledDeviceExtensions.data();
251		deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
252		deviceCreateInfo.queueCreateInfoCount = 1;
253		deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
254		res = vkCreateDevice(vkGPU->physicalDevice, &deviceCreateInfo, NULL, &vkGPU->device);
255		if (res != VK_SUCCESS) return res;
256		vkGetDeviceQueue(vkGPU->device, (uint32_t)vkGPU->queueFamilyIndex, 0, &vkGPU->queue);
257		break;
258	}
259#if (VK_API_VERSION>10)
260	case 2: case 102: case 202: case 1002: {
261		VkPhysicalDeviceFeatures2 deviceFeatures2 = {};
262		VkPhysicalDevice16BitStorageFeatures shaderFloat16 = {};
263		shaderFloat16.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
264		shaderFloat16.storageBuffer16BitAccess = true;
265		/*VkPhysicalDeviceShaderFloat16Int8Features shaderFloat16 = {};
266		shaderFloat16.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
267		shaderFloat16.shaderFloat16 = true;
268		shaderFloat16.shaderInt8 = true;*/
269		deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
270		deviceFeatures2.pNext = &shaderFloat16;
271		deviceFeatures2.features = deviceFeatures;
272		vkGetPhysicalDeviceFeatures2(vkGPU->physicalDevice, &deviceFeatures2);
273		deviceCreateInfo.pNext = &deviceFeatures2;
274		vkGPU->enabledDeviceExtensions.push_back("VK_KHR_16bit_storage");
275		deviceCreateInfo.enabledExtensionCount = (uint32_t)vkGPU->enabledDeviceExtensions.size();
276		deviceCreateInfo.ppEnabledExtensionNames = vkGPU->enabledDeviceExtensions.data();
277		deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
278		deviceCreateInfo.queueCreateInfoCount = 1;
279		deviceCreateInfo.pEnabledFeatures = NULL;
280		res = vkCreateDevice(vkGPU->physicalDevice, &deviceCreateInfo, NULL, &vkGPU->device);
281		if (res != VK_SUCCESS) return res;
282		vkGetDeviceQueue(vkGPU->device, (uint32_t)vkGPU->queueFamilyIndex, 0, &vkGPU->queue);
283		break;
284	}
285#endif
286	default: {
287		deviceCreateInfo.enabledExtensionCount = (uint32_t)vkGPU->enabledDeviceExtensions.size();
288		deviceCreateInfo.ppEnabledExtensionNames = vkGPU->enabledDeviceExtensions.data();
289		deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
290		deviceCreateInfo.queueCreateInfoCount = 1;
291		deviceCreateInfo.pEnabledFeatures = NULL;
292		deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
293		res = vkCreateDevice(vkGPU->physicalDevice, &deviceCreateInfo, NULL, &vkGPU->device);
294		if (res != VK_SUCCESS) return res;
295		vkGetDeviceQueue(vkGPU->device, (uint32_t)vkGPU->queueFamilyIndex, 0, &vkGPU->queue);
296		break;
297	}
298	}
299	return res;
300}
301VkResult createFence(VkGPU* vkGPU) {
302	//create fence for synchronization 
303	VkResult res = VK_SUCCESS;
304	VkFenceCreateInfo fenceCreateInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
305	fenceCreateInfo.flags = 0;
306	res = vkCreateFence(vkGPU->device, &fenceCreateInfo, NULL, &vkGPU->fence);
307	return res;
308}
309VkResult createCommandPool(VkGPU* vkGPU) {
310	//create a place, command buffer memory is allocated from
311	VkResult res = VK_SUCCESS;
312	VkCommandPoolCreateInfo commandPoolCreateInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
313	commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
314	commandPoolCreateInfo.queueFamilyIndex = (uint32_t)vkGPU->queueFamilyIndex;
315	res = vkCreateCommandPool(vkGPU->device, &commandPoolCreateInfo, NULL, &vkGPU->commandPool);
316	return res;
317}
318
319VkFFTResult findMemoryType(VkGPU* vkGPU, uint64_t memoryTypeBits, uint64_t memorySize, VkMemoryPropertyFlags properties, uint32_t* memoryTypeIndex) {
320	VkPhysicalDeviceMemoryProperties memoryProperties = { 0 };
321
322	vkGetPhysicalDeviceMemoryProperties(vkGPU->physicalDevice, &memoryProperties);
323
324	for (uint64_t i = 0; i < memoryProperties.memoryTypeCount; ++i) {
325		if ((memoryTypeBits & ((uint64_t)1 << i)) && ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) && (memoryProperties.memoryHeaps[memoryProperties.memoryTypes[i].heapIndex].size >= memorySize))
326		{
327			memoryTypeIndex[0] = (uint32_t)i;
328			return VKFFT_SUCCESS;
329		}
330	}
331	return VKFFT_ERROR_FAILED_TO_FIND_MEMORY;
332}
333
334VkFFTResult allocateBuffer(VkGPU* vkGPU, VkBuffer* buffer, VkDeviceMemory* deviceMemory, VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags propertyFlags, uint64_t size) {
335	//allocate the buffer used by the GPU with specified properties
336	VkFFTResult resFFT = VKFFT_SUCCESS;
337	VkResult res = VK_SUCCESS;
338	uint32_t queueFamilyIndices;
339	VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
340	bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
341	bufferCreateInfo.queueFamilyIndexCount = 1;
342	bufferCreateInfo.pQueueFamilyIndices = &queueFamilyIndices;
343	bufferCreateInfo.size = size;
344	bufferCreateInfo.usage = usageFlags;
345	res = vkCreateBuffer(vkGPU->device, &bufferCreateInfo, NULL, buffer);
346	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_CREATE_BUFFER;
347	VkMemoryRequirements memoryRequirements = { 0 };
348	vkGetBufferMemoryRequirements(vkGPU->device, buffer[0], &memoryRequirements);
349	VkMemoryAllocateInfo memoryAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
350	memoryAllocateInfo.allocationSize = memoryRequirements.size;
351	resFFT = findMemoryType(vkGPU, memoryRequirements.memoryTypeBits, memoryRequirements.size, propertyFlags, &memoryAllocateInfo.memoryTypeIndex);
352	if (resFFT != VKFFT_SUCCESS) return resFFT;
353	res = vkAllocateMemory(vkGPU->device, &memoryAllocateInfo, NULL, deviceMemory);
354	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_ALLOCATE_MEMORY;
355	res = vkBindBufferMemory(vkGPU->device, buffer[0], deviceMemory[0], 0);
356	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_BIND_BUFFER_MEMORY;
357	return resFFT;
358}
359#endif
360VkFFTResult transferDataToCPU(VkGPU* vkGPU, void* cpu_arr, void* output_buffer, uint64_t transferSize) {
361	//a function that transfers data from the GPU to the CPU using staging buffer, because the GPU memory is not host-coherent
362	VkFFTResult resFFT = VKFFT_SUCCESS;
363#if(VKFFT_BACKEND==0)
364	VkResult res = VK_SUCCESS;
365	VkBuffer* buffer = (VkBuffer*)output_buffer;
366	uint64_t stagingBufferSize = transferSize;
367	VkBuffer* stagingBuffer = {0};
368	VkDeviceMemory* stagingBufferMemory = {0};
369	if (!vkGPU->stagingBuffer){
370		stagingBuffer = (VkBuffer*)calloc(1, sizeof(VkBuffer));
371		stagingBufferMemory = (VkDeviceMemory*)calloc(1, sizeof(VkDeviceMemory));
372		resFFT = allocateBuffer(vkGPU, stagingBuffer, stagingBufferMemory, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBufferSize);
373		if (resFFT != VKFFT_SUCCESS) return resFFT;
374	}else{
375		stagingBuffer = vkGPU->stagingBuffer;
376		stagingBufferMemory = vkGPU->stagingBufferMemory;
377	}
378	VkCommandBufferAllocateInfo commandBufferAllocateInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
379	commandBufferAllocateInfo.commandPool = vkGPU->commandPool;
380	commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
381	commandBufferAllocateInfo.commandBufferCount = 1;
382	VkCommandBuffer commandBuffer = { 0 };
383	res = vkAllocateCommandBuffers(vkGPU->device, &commandBufferAllocateInfo, &commandBuffer);
384	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_ALLOCATE_COMMAND_BUFFERS;
385	VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
386	commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
387	res = vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo);
388	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_BEGIN_COMMAND_BUFFER;
389	VkBufferCopy copyRegion = { 0 };
390	copyRegion.srcOffset = 0;
391	copyRegion.dstOffset = 0;
392	copyRegion.size = stagingBufferSize;
393	vkCmdCopyBuffer(commandBuffer, buffer[0], stagingBuffer[0], 1, &copyRegion);
394	res = vkEndCommandBuffer(commandBuffer);
395	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_END_COMMAND_BUFFER;
396	VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
397	submitInfo.commandBufferCount = 1;
398	submitInfo.pCommandBuffers = &commandBuffer;
399	res = vkQueueSubmit(vkGPU->queue, 1, &submitInfo, vkGPU->fence);
400	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_SUBMIT_QUEUE;
401	res = vkWaitForFences(vkGPU->device, 1, &vkGPU->fence, VK_TRUE, 100000000000);
402	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_WAIT_FOR_FENCES;
403	res = vkResetFences(vkGPU->device, 1, &vkGPU->fence);
404	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_RESET_FENCES;
405	vkFreeCommandBuffers(vkGPU->device, vkGPU->commandPool, 1, &commandBuffer);
406	void* data;
407	res = vkMapMemory(vkGPU->device, stagingBufferMemory[0], 0, stagingBufferSize, 0, &data);
408	if (resFFT != VKFFT_SUCCESS) return resFFT;
409	memcpy(cpu_arr, data, stagingBufferSize);
410	vkUnmapMemory(vkGPU->device, stagingBufferMemory[0]);
411	if (!vkGPU->stagingBuffer){
412		vkDestroyBuffer(vkGPU->device, stagingBuffer[0], 0);
413		vkFreeMemory(vkGPU->device, stagingBufferMemory[0], 0);
414		free(stagingBuffer);
415		free(stagingBufferMemory);
416	}
417#elif(VKFFT_BACKEND==1)
418	cudaError_t res = cudaSuccess;
419	void* buffer = ((void**)output_buffer)[0];
420	res = cudaMemcpy(cpu_arr, buffer, transferSize, cudaMemcpyDeviceToHost);
421	if (res != cudaSuccess) {
422		return VKFFT_ERROR_FAILED_TO_COPY;
423	}
424#elif(VKFFT_BACKEND==2)
425	hipError_t res = hipSuccess;
426	void* buffer = ((void**)output_buffer)[0];
427	res = hipMemcpy(cpu_arr, buffer, transferSize, hipMemcpyDeviceToHost);
428	if (res != hipSuccess) {
429		return VKFFT_ERROR_FAILED_TO_COPY;
430	}
431#elif(VKFFT_BACKEND==3)
432	cl_int res = CL_SUCCESS;
433	cl_mem* buffer = (cl_mem*)output_buffer;
434	cl_command_queue commandQueue = clCreateCommandQueue(vkGPU->context, vkGPU->device, 0, &res);
435	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_QUEUE;
436	res = clEnqueueReadBuffer(commandQueue, buffer[0], CL_TRUE, 0, transferSize, cpu_arr, 0, NULL, NULL);
437	if (res != CL_SUCCESS) {
438		return VKFFT_ERROR_FAILED_TO_COPY;
439	}
440	res = clReleaseCommandQueue(commandQueue);
441	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_RELEASE_COMMAND_QUEUE;
442#elif(VKFFT_BACKEND==4)
443	ze_result_t res = ZE_RESULT_SUCCESS;
444	void* buffer = ((void**)output_buffer)[0];
445	ze_command_queue_desc_t commandQueueCopyDesc = {
446			ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC,
447			0,
448			vkGPU->commandQueueID,
449			0, // index
450			0, // flags
451			ZE_COMMAND_QUEUE_MODE_DEFAULT,
452			ZE_COMMAND_QUEUE_PRIORITY_NORMAL
453	};
454	ze_command_list_handle_t copyCommandList;
455	res = zeCommandListCreateImmediate(vkGPU->context, vkGPU->device, &commandQueueCopyDesc, &copyCommandList);
456	if (res != ZE_RESULT_SUCCESS) {
457		return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
458	}
459	res = zeCommandListAppendMemoryCopy(copyCommandList, cpu_arr, buffer, transferSize, 0, 0, 0);
460	if (res != ZE_RESULT_SUCCESS) {
461		return VKFFT_ERROR_FAILED_TO_COPY;
462	}
463	res = zeCommandQueueSynchronize(vkGPU->commandQueue, UINT32_MAX);
464	if (res != ZE_RESULT_SUCCESS) {
465		return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
466	}
467#elif(VKFFT_BACKEND==5)
468	MTL::Buffer* stagingBuffer = vkGPU->device->newBuffer(transferSize, MTL::ResourceStorageModeShared);
469	MTL::CommandBuffer* copyCommandBuffer = vkGPU->queue->commandBuffer();
470	if (copyCommandBuffer == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
471	MTL::BlitCommandEncoder* blitCommandEncoder = copyCommandBuffer->blitCommandEncoder();
472	if (blitCommandEncoder == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
473	MTL::Buffer* buffer = ((MTL::Buffer**)output_buffer)[0];
474	blitCommandEncoder->copyFromBuffer((MTL::Buffer*)buffer, 0, (MTL::Buffer*)stagingBuffer, 0, transferSize);
475	blitCommandEncoder->endEncoding();
476	copyCommandBuffer->commit();
477	copyCommandBuffer->waitUntilCompleted();
478	blitCommandEncoder->release();
479	copyCommandBuffer->release();
480	memcpy(cpu_arr, stagingBuffer->contents(), transferSize);
481	stagingBuffer->release();
482#endif
483	return resFFT;
484}
485VkFFTResult transferDataFromCPU(VkGPU* vkGPU, void* cpu_arr, void* input_buffer, uint64_t transferSize) {
486	VkFFTResult resFFT = VKFFT_SUCCESS;
487#if(VKFFT_BACKEND==0)
488	VkResult res = VK_SUCCESS;
489	VkBuffer* buffer = (VkBuffer*)input_buffer;
490	uint64_t stagingBufferSize = transferSize;
491	VkBuffer* stagingBuffer = {0};
492	VkDeviceMemory* stagingBufferMemory = {0};
493	if (!vkGPU->stagingBuffer){
494		stagingBuffer = (VkBuffer*)calloc(1, sizeof(VkBuffer));
495		stagingBufferMemory = (VkDeviceMemory*)calloc(1, sizeof(VkDeviceMemory));
496		resFFT = allocateBuffer(vkGPU, stagingBuffer, stagingBufferMemory, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBufferSize);
497		if (resFFT != VKFFT_SUCCESS) return resFFT;
498	}else{
499		stagingBuffer = vkGPU->stagingBuffer;
500		stagingBufferMemory = vkGPU->stagingBufferMemory;
501	}
502	void* data;
503	res = vkMapMemory(vkGPU->device, stagingBufferMemory[0], 0, stagingBufferSize, 0, &data);
504	if (resFFT != VKFFT_SUCCESS) return resFFT;
505	memcpy(data, cpu_arr, stagingBufferSize);
506	vkUnmapMemory(vkGPU->device, stagingBufferMemory[0]);
507	VkCommandBufferAllocateInfo commandBufferAllocateInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
508	commandBufferAllocateInfo.commandPool = vkGPU->commandPool;
509	commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
510	commandBufferAllocateInfo.commandBufferCount = 1;
511	VkCommandBuffer commandBuffer = { 0 };
512	res = vkAllocateCommandBuffers(vkGPU->device, &commandBufferAllocateInfo, &commandBuffer);
513	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_ALLOCATE_COMMAND_BUFFERS;
514	VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
515	commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
516	res = vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo);
517	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_BEGIN_COMMAND_BUFFER;
518	VkBufferCopy copyRegion = { 0 };
519	copyRegion.srcOffset = 0;
520	copyRegion.dstOffset = 0;
521	copyRegion.size = stagingBufferSize;
522	vkCmdCopyBuffer(commandBuffer, stagingBuffer[0], buffer[0], 1, &copyRegion);
523	res = vkEndCommandBuffer(commandBuffer);
524	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_END_COMMAND_BUFFER;
525	VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
526	submitInfo.commandBufferCount = 1;
527	submitInfo.pCommandBuffers = &commandBuffer;
528	res = vkQueueSubmit(vkGPU->queue, 1, &submitInfo, vkGPU->fence);
529	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_SUBMIT_QUEUE;
530	res = vkWaitForFences(vkGPU->device, 1, &vkGPU->fence, VK_TRUE, 100000000000);
531	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_WAIT_FOR_FENCES;
532	res = vkResetFences(vkGPU->device, 1, &vkGPU->fence);
533	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_RESET_FENCES;
534	vkFreeCommandBuffers(vkGPU->device, vkGPU->commandPool, 1, &commandBuffer);
535	if (!vkGPU->stagingBuffer){
536		vkDestroyBuffer(vkGPU->device, stagingBuffer[0], 0);
537		vkFreeMemory(vkGPU->device, stagingBufferMemory[0], 0);
538		free(stagingBuffer);
539		free(stagingBufferMemory);
540	}
541	return resFFT;
542#elif(VKFFT_BACKEND==1)
543	cudaError_t res = cudaSuccess;
544	void* buffer = ((void**)input_buffer)[0];
545	res = cudaMemcpy(buffer, cpu_arr, transferSize, cudaMemcpyHostToDevice);
546	if (res != cudaSuccess) {
547		return VKFFT_ERROR_FAILED_TO_COPY;
548	}
549#elif(VKFFT_BACKEND==2)
550	hipError_t res = hipSuccess;
551	void* buffer = ((void**)input_buffer)[0];
552	res = hipMemcpy(buffer, cpu_arr, transferSize, hipMemcpyHostToDevice);
553	if (res != hipSuccess) {
554		return VKFFT_ERROR_FAILED_TO_COPY;
555	}
556#elif(VKFFT_BACKEND==3)
557	cl_int res = CL_SUCCESS;
558	cl_mem* buffer = (cl_mem*)input_buffer;
559	cl_command_queue commandQueue = clCreateCommandQueue(vkGPU->context, vkGPU->device, 0, &res);
560	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_QUEUE;
561	res = clEnqueueWriteBuffer(commandQueue, buffer[0], CL_TRUE, 0, transferSize, cpu_arr, 0, NULL, NULL);
562	if (res != CL_SUCCESS) {
563		return VKFFT_ERROR_FAILED_TO_COPY;
564	}
565	res = clReleaseCommandQueue(commandQueue);
566	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_RELEASE_COMMAND_QUEUE;
567#elif(VKFFT_BACKEND==4)
568	ze_result_t res = ZE_RESULT_SUCCESS;
569	void* buffer = ((void**)input_buffer)[0];
570	ze_command_queue_desc_t commandQueueCopyDesc = {
571			ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC,
572			0,
573			vkGPU->commandQueueID,
574			0, // index
575			0, // flags
576			ZE_COMMAND_QUEUE_MODE_DEFAULT,
577			ZE_COMMAND_QUEUE_PRIORITY_NORMAL
578	};
579	ze_command_list_handle_t copyCommandList;
580	res = zeCommandListCreateImmediate(vkGPU->context, vkGPU->device, &commandQueueCopyDesc, &copyCommandList);
581	if (res != ZE_RESULT_SUCCESS) {
582		return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
583	}
584	res = zeCommandListAppendMemoryCopy(copyCommandList, buffer, cpu_arr, transferSize, 0, 0, 0);
585	if (res != ZE_RESULT_SUCCESS) {
586		return VKFFT_ERROR_FAILED_TO_COPY;
587	}
588	res = zeCommandQueueSynchronize(vkGPU->commandQueue, UINT32_MAX);
589	if (res != ZE_RESULT_SUCCESS) {
590		return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
591	}
592#elif(VKFFT_BACKEND==5)
593	MTL::Buffer* stagingBuffer = vkGPU->device->newBuffer(cpu_arr, transferSize, MTL::ResourceStorageModeShared);
594	MTL::CommandBuffer* copyCommandBuffer = vkGPU->queue->commandBuffer();
595	if (copyCommandBuffer == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
596	MTL::BlitCommandEncoder* blitCommandEncoder = copyCommandBuffer->blitCommandEncoder();
597	if (blitCommandEncoder == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
598	MTL::Buffer* buffer = ((MTL::Buffer**)input_buffer)[0];
599	blitCommandEncoder->copyFromBuffer((MTL::Buffer*)stagingBuffer, 0, (MTL::Buffer*)buffer, 0, transferSize);
600	blitCommandEncoder->endEncoding();
601	copyCommandBuffer->commit();
602	copyCommandBuffer->waitUntilCompleted();
603	blitCommandEncoder->release();
604	copyCommandBuffer->release();
605	stagingBuffer->release();
606#endif
607	return resFFT;
608}
609VkFFTResult devices_list() {
610	//this function creates an instance and prints the list of available devices
611#if(VKFFT_BACKEND==0)
612	VkResult res = VK_SUCCESS;
613	VkInstance local_instance = { 0 };
614	VkInstanceCreateInfo createInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
615	createInfo.flags = 0;
616	createInfo.pApplicationInfo = NULL;
617	VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };
618	createInfo.enabledLayerCount = 0;
619	createInfo.enabledExtensionCount = 0;
620	createInfo.pNext = NULL;
621	res = vkCreateInstance(&createInfo, NULL, &local_instance);
622	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_CREATE_INSTANCE;
623
624	uint32_t deviceCount;
625	res = vkEnumeratePhysicalDevices(local_instance, &deviceCount, NULL);
626	if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_ENUMERATE_DEVICES;
627
628	VkPhysicalDevice* devices = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * deviceCount);
629	if (!devices) return VKFFT_ERROR_MALLOC_FAILED;
630	if (devices) {
631		res = vkEnumeratePhysicalDevices(local_instance, &deviceCount, devices);
632		if (res != VK_SUCCESS) return VKFFT_ERROR_FAILED_TO_ENUMERATE_DEVICES;
633		for (uint64_t i = 0; i < deviceCount; i++) {
634			VkPhysicalDeviceProperties device_properties;
635			vkGetPhysicalDeviceProperties(devices[i], &device_properties);
636			printf("Device id: %" PRIu64 " name: %s API:%d.%d.%d\n", i, device_properties.deviceName, (device_properties.apiVersion >> 22), ((device_properties.apiVersion >> 12) & 0x3ff), (device_properties.apiVersion & 0xfff));
637		}
638		free(devices);
639	}
640	else
641		return VKFFT_ERROR_FAILED_TO_ENUMERATE_DEVICES;
642	vkDestroyInstance(local_instance, NULL);
643#elif(VKFFT_BACKEND==1)
644	CUresult res = CUDA_SUCCESS;
645	res = cuInit(0);
646	if (res != CUDA_SUCCESS) return VKFFT_ERROR_FAILED_TO_INITIALIZE;
647	int numDevices;
648	res = cuDeviceGetCount(&numDevices);
649	if (res != CUDA_SUCCESS) return VKFFT_ERROR_FAILED_TO_SET_DEVICE_ID;
650	for (uint64_t i = 0; i < numDevices; i++) {
651		char deviceName[256];
652		CUdevice device = {};
653		res = cuDeviceGet(&device, (int)i);
654		if (res != CUDA_SUCCESS) return VKFFT_ERROR_FAILED_TO_GET_DEVICE;
655		res = cuDeviceGetName(deviceName, 256, device);
656		if (res != CUDA_SUCCESS) return VKFFT_ERROR_FAILED_TO_GET_DEVICE;
657		printf("Device id: %" PRIu64 " name: %s\n", i, deviceName);
658	}
659#elif(VKFFT_BACKEND==2)
660	hipError_t res = hipSuccess;
661	res = hipInit(0);
662	if (res != hipSuccess) return VKFFT_ERROR_FAILED_TO_INITIALIZE;
663	int numDevices;
664	res = hipGetDeviceCount(&numDevices);
665	if (res != hipSuccess) return VKFFT_ERROR_FAILED_TO_SET_DEVICE_ID;
666	for (uint64_t i = 0; i < numDevices; i++) {
667		char deviceName[256];
668		hipDevice_t device = {};
669		res = hipDeviceGet(&device, i);
670		if (res != hipSuccess) return VKFFT_ERROR_FAILED_TO_GET_DEVICE;
671		res = hipDeviceGetName(deviceName, 256, device);
672		if (res != hipSuccess) return VKFFT_ERROR_FAILED_TO_GET_DEVICE;
673		printf("Device id: %" PRIu64 " name: %s\n", i, deviceName);
674	}
675#elif(VKFFT_BACKEND==3)
676	cl_int res = CL_SUCCESS;
677	cl_uint numPlatforms;
678	res = clGetPlatformIDs(0, 0, &numPlatforms);
679	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_INITIALIZE;
680	cl_platform_id* platforms = (cl_platform_id*)malloc(sizeof(cl_platform_id) * numPlatforms);
681	if (!platforms) return VKFFT_ERROR_MALLOC_FAILED;
682	res = clGetPlatformIDs(numPlatforms, platforms, 0);
683	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_INITIALIZE;
684	uint64_t k = 0;
685	for (uint64_t j = 0; j < numPlatforms; j++) {
686		cl_uint numDevices;
687		res = clGetDeviceIDs(platforms[j], CL_DEVICE_TYPE_ALL, 0, 0, &numDevices);
688		cl_device_id* deviceList = (cl_device_id*)malloc(sizeof(cl_device_id) * numDevices);
689		if (!deviceList) return VKFFT_ERROR_MALLOC_FAILED;
690		res = clGetDeviceIDs(platforms[j], CL_DEVICE_TYPE_ALL, numDevices, deviceList, 0);
691		if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_GET_DEVICE;
692		for (uint64_t i = 0; i < numDevices; i++) {
693			char deviceName[256];
694			char apiVersion[256];
695			res = clGetDeviceInfo(deviceList[i], CL_DEVICE_NAME, 256 * sizeof(char), deviceName, 0);
696			if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_ENUMERATE_DEVICES;
697			res = clGetDeviceInfo(deviceList[i], CL_DEVICE_VERSION, 256 * sizeof(char), apiVersion, 0);
698			if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_ENUMERATE_DEVICES;
699			printf("Platform id: %" PRIu64 " Device id: %" PRIu64 " name: %s API:%s\n", j, k, deviceName, apiVersion);
700			k++;
701		}
702		free(deviceList);
703	}
704	free(platforms);
705#elif(VKFFT_BACKEND==4)
706	ze_result_t res = ZE_RESULT_SUCCESS;
707	res = zeInit(0);
708	if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_INITIALIZE;
709	uint32_t numDrivers = 0;
710	res = zeDriverGet(&numDrivers, 0);
711	if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_INITIALIZE;
712	ze_driver_handle_t* drivers = (ze_driver_handle_t*)malloc(numDrivers * sizeof(ze_driver_handle_t));
713	if (!drivers) return VKFFT_ERROR_MALLOC_FAILED;
714	res = zeDriverGet(&numDrivers, drivers);
715	if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_INITIALIZE;
716	uint64_t k = 0;
717	for (uint64_t j = 0; j < numDrivers; j++) {
718		uint32_t numDevices = 0;
719		res = zeDeviceGet(drivers[j], &numDevices, nullptr);
720		if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_GET_DEVICE;
721		ze_device_handle_t* deviceList = (ze_device_handle_t*)malloc(numDevices * sizeof(ze_device_handle_t));
722		if (!deviceList) return VKFFT_ERROR_MALLOC_FAILED;
723		res = zeDeviceGet(drivers[j], &numDevices, deviceList);
724		if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_GET_DEVICE;
725		for (uint64_t i = 0; i < numDevices; i++) {
726			ze_device_properties_t device_properties;
727			res = zeDeviceGetProperties(deviceList[i], &device_properties);
728			if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_ENUMERATE_DEVICES;
729			printf("Driver id: %" PRIu64 " Device id: %" PRIu64 " name: %s\n", j, k, device_properties.name);
730			k++;
731		}
732
733		free(deviceList);
734	}
735	free(drivers);
736#elif(VKFFT_BACKEND==5)
737	NS::Array* devices = MTL::CopyAllDevices();
738	for (uint64_t i = 0; i < devices->count(); i++) {
739		MTL::Device* loc_device = (MTL::Device*)devices->object(i);
740		printf("Device id: %" PRIu64 " name: %s\n", i, loc_device->name()->cString(NS::UTF8StringEncoding));
741	}
742#endif
743	return VKFFT_SUCCESS;
744}
745VkFFTResult performVulkanFFT(VkGPU* vkGPU, VkFFTApplication* app, VkFFTLaunchParams* launchParams, int inverse, uint64_t num_iter) {
746	VkFFTResult resFFT = VKFFT_SUCCESS;
747#if(VKFFT_BACKEND==0)
748	VkResult res = VK_SUCCESS;
749	VkCommandBufferAllocateInfo commandBufferAllocateInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
750	commandBufferAllocateInfo.commandPool = vkGPU->commandPool;
751	commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
752	commandBufferAllocateInfo.commandBufferCount = 1;
753	VkCommandBuffer commandBuffer = {};
754	res = vkAllocateCommandBuffers(vkGPU->device, &commandBufferAllocateInfo, &commandBuffer);
755	if (res != 0) return VKFFT_ERROR_FAILED_TO_ALLOCATE_COMMAND_BUFFERS;
756	VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
757	commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
758	res = vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo);
759	if (res != 0) return VKFFT_ERROR_FAILED_TO_BEGIN_COMMAND_BUFFER;
760	launchParams->commandBuffer = &commandBuffer;
761	//Record commands num_iter times. Allows to perform multiple convolutions/transforms in one submit.
762	for (uint64_t i = 0; i < num_iter; i++) {
763		resFFT = VkFFTAppend(app, inverse, launchParams);
764		if (resFFT != VKFFT_SUCCESS) return resFFT;
765	}
766	res = vkEndCommandBuffer(commandBuffer);
767	if (res != 0) return VKFFT_ERROR_FAILED_TO_END_COMMAND_BUFFER;
768	VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
769	submitInfo.commandBufferCount = 1;
770	submitInfo.pCommandBuffers = &commandBuffer;
771	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
772	res = vkQueueSubmit(vkGPU->queue, 1, &submitInfo, vkGPU->fence);
773	if (res != 0) return VKFFT_ERROR_FAILED_TO_SUBMIT_QUEUE;
774	res = vkWaitForFences(vkGPU->device, 1, &vkGPU->fence, VK_TRUE, 100000000000);
775	if (res != 0) return VKFFT_ERROR_FAILED_TO_WAIT_FOR_FENCES;
776	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
777	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
778	//printf("Pure submit execution time per num_iter: %.3f ms\n", totTime / num_iter);
779	res = vkResetFences(vkGPU->device, 1, &vkGPU->fence);
780	if (res != 0) return VKFFT_ERROR_FAILED_TO_RESET_FENCES;
781	vkFreeCommandBuffers(vkGPU->device, vkGPU->commandPool, 1, &commandBuffer);
782#elif(VKFFT_BACKEND==1)
783	cudaError_t res = cudaSuccess;
784	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
785	for (uint64_t i = 0; i < num_iter; i++) {
786		resFFT = VkFFTAppend(app, inverse, launchParams);
787		if (resFFT != VKFFT_SUCCESS) return resFFT;
788	}
789	res = cudaDeviceSynchronize();
790	if (res != cudaSuccess) return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
791	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
792	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
793#elif(VKFFT_BACKEND==2)
794	hipError_t res = hipSuccess;
795	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
796	for (uint64_t i = 0; i < num_iter; i++) {
797		resFFT = VkFFTAppend(app, inverse, launchParams);
798		if (resFFT != VKFFT_SUCCESS) return resFFT;
799	}
800	res = hipDeviceSynchronize();
801	if (res != hipSuccess) return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
802	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
803	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
804#elif(VKFFT_BACKEND==3)
805	cl_int res = CL_SUCCESS;
806	launchParams->commandQueue = &vkGPU->commandQueue;
807	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
808	for (uint64_t i = 0; i < num_iter; i++) {
809		resFFT = VkFFTAppend(app, inverse, launchParams);
810		if (resFFT != VKFFT_SUCCESS) return resFFT;
811	}
812	res = clFinish(vkGPU->commandQueue);
813	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
814	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
815	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
816#elif(VKFFT_BACKEND==4)
817	ze_result_t res = ZE_RESULT_SUCCESS;
818	ze_command_list_desc_t commandListDescription = {};
819	commandListDescription.stype = ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC;
820	ze_command_list_handle_t commandList = {};
821	res = zeCommandListCreate(vkGPU->context, vkGPU->device, &commandListDescription, &commandList);
822	if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
823
824	launchParams->commandList = &commandList;
825	//Record commands num_iter times. Allows to perform multiple convolutions/transforms in one submit.
826	for (uint64_t i = 0; i < num_iter; i++) {
827		resFFT = VkFFTAppend(app, inverse, launchParams);
828		if (resFFT != VKFFT_SUCCESS) return resFFT;
829	}
830	res = zeCommandListClose(commandList);
831	if (res != 0) return VKFFT_ERROR_FAILED_TO_END_COMMAND_BUFFER;
832
833	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
834	res = zeCommandQueueExecuteCommandLists(vkGPU->commandQueue, 1, &commandList, 0);
835	if (res != 0) return VKFFT_ERROR_FAILED_TO_SUBMIT_QUEUE;
836	res = zeCommandQueueSynchronize(vkGPU->commandQueue, UINT32_MAX);
837	if (res != 0) return VKFFT_ERROR_FAILED_TO_WAIT_FOR_FENCES;
838
839	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
840	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
841	//printf("Pure submit execution time per num_iter: %.3f ms\n", totTime / num_iter);
842	res = zeCommandListDestroy(commandList);
843	if (res != 0) return VKFFT_ERROR_FAILED_TO_DESTROY_COMMAND_LIST;
844#elif(VKFFT_BACKEND==5)
845	MTL::CommandBuffer* commandBuffer = vkGPU->queue->commandBuffer();
846	if (commandBuffer == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
847	launchParams->commandBuffer = commandBuffer;
848	MTL::ComputeCommandEncoder* commandEncoder = commandBuffer->computeCommandEncoder();
849	if (commandEncoder == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
850	launchParams->commandEncoder = commandEncoder;
851	for (uint64_t i = 0; i < num_iter; i++) {
852		resFFT = VkFFTAppend(app, inverse, launchParams);
853		if (resFFT != VKFFT_SUCCESS) return resFFT;
854	}
855	commandEncoder->endEncoding();
856
857	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
858	commandBuffer->commit();
859	commandBuffer->waitUntilCompleted();
860	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
861	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
862
863	commandEncoder->release();
864	commandBuffer->release();
865#endif
866	return resFFT;
867}
868VkFFTResult performVulkanFFTiFFT(VkGPU* vkGPU, VkFFTApplication* app, VkFFTLaunchParams* launchParams, uint64_t num_iter, double* time_result) {
869	VkFFTResult resFFT = VKFFT_SUCCESS;
870#if(VKFFT_BACKEND==0)
871	VkResult res = VK_SUCCESS;
872	VkCommandBufferAllocateInfo commandBufferAllocateInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
873	commandBufferAllocateInfo.commandPool = vkGPU->commandPool;
874	commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
875	commandBufferAllocateInfo.commandBufferCount = 1;
876	VkCommandBuffer commandBuffer = {};
877	res = vkAllocateCommandBuffers(vkGPU->device, &commandBufferAllocateInfo, &commandBuffer);
878	if (res != 0) return VKFFT_ERROR_FAILED_TO_ALLOCATE_COMMAND_BUFFERS;
879	VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
880	commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
881	res = vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo);
882	if (res != 0) return VKFFT_ERROR_FAILED_TO_BEGIN_COMMAND_BUFFER;
883	launchParams->commandBuffer = &commandBuffer;
884	for (uint64_t i = 0; i < num_iter; i++) {
885		resFFT = VkFFTAppend(app, -1, launchParams);
886		if (resFFT != VKFFT_SUCCESS) return resFFT;
887		resFFT = VkFFTAppend(app, 1, launchParams);
888		if (resFFT != VKFFT_SUCCESS) return resFFT;
889	}
890	res = vkEndCommandBuffer(commandBuffer);
891	if (res != 0) return VKFFT_ERROR_FAILED_TO_END_COMMAND_BUFFER;
892	VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
893	submitInfo.commandBufferCount = 1;
894	submitInfo.pCommandBuffers = &commandBuffer;
895	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
896	res = vkQueueSubmit(vkGPU->queue, 1, &submitInfo, vkGPU->fence);
897	if (res != 0) return VKFFT_ERROR_FAILED_TO_SUBMIT_QUEUE;
898	res = vkWaitForFences(vkGPU->device, 1, &vkGPU->fence, VK_TRUE, 100000000000);
899	if (res != 0) return VKFFT_ERROR_FAILED_TO_WAIT_FOR_FENCES;
900	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
901	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
902	time_result[0] = totTime / num_iter;
903	res = vkResetFences(vkGPU->device, 1, &vkGPU->fence);
904	if (res != 0) return VKFFT_ERROR_FAILED_TO_RESET_FENCES;
905	vkFreeCommandBuffers(vkGPU->device, vkGPU->commandPool, 1, &commandBuffer);
906#elif(VKFFT_BACKEND==1)
907	cudaError_t res = cudaSuccess;
908	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
909	for (uint64_t i = 0; i < num_iter; i++) {
910		resFFT = VkFFTAppend(app, -1, launchParams);
911		if (resFFT != VKFFT_SUCCESS) return resFFT;
912		resFFT = VkFFTAppend(app, 1, launchParams);
913		if (resFFT != VKFFT_SUCCESS) return resFFT;
914	}
915	res = cudaDeviceSynchronize();
916	if (res != cudaSuccess) return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
917	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
918	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
919	time_result[0] = totTime / num_iter;
920#elif(VKFFT_BACKEND==2)
921	hipError_t res = hipSuccess;
922	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
923	for (uint64_t i = 0; i < num_iter; i++) {
924		resFFT = VkFFTAppend(app, -1, launchParams);
925		if (resFFT != VKFFT_SUCCESS) return resFFT;
926		resFFT = VkFFTAppend(app, 1, launchParams);
927		if (resFFT != VKFFT_SUCCESS) return resFFT;
928	}
929	res = hipDeviceSynchronize();
930	if (res != hipSuccess) return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
931	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
932	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
933	time_result[0] = totTime / num_iter;
934#elif(VKFFT_BACKEND==3)
935	cl_int res = CL_SUCCESS;
936	launchParams->commandQueue = &vkGPU->commandQueue;
937	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
938	for (uint64_t i = 0; i < num_iter; i++) {
939		resFFT = VkFFTAppend(app, -1, launchParams);
940		if (resFFT != VKFFT_SUCCESS) return resFFT;
941		resFFT = VkFFTAppend(app, 1, launchParams);
942		if (resFFT != VKFFT_SUCCESS) return resFFT;
943	}
944	res = clFinish(vkGPU->commandQueue);
945	if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_SYNCHRONIZE;
946	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
947	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
948	time_result[0] = totTime / num_iter;
949#elif(VKFFT_BACKEND==4)
950	ze_result_t res = ZE_RESULT_SUCCESS;
951	ze_command_list_desc_t commandListDescription = {};
952	commandListDescription.stype = ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC;
953	ze_command_list_handle_t commandList = {};
954	res = zeCommandListCreate(vkGPU->context, vkGPU->device, &commandListDescription, &commandList);
955	if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
956
957	launchParams->commandList = &commandList;
958	for (uint64_t i = 0; i < num_iter; i++) {
959		resFFT = VkFFTAppend(app, -1, launchParams);
960		if (resFFT != VKFFT_SUCCESS) return resFFT;
961		resFFT = VkFFTAppend(app, 1, launchParams);
962		if (resFFT != VKFFT_SUCCESS) return resFFT;
963	}
964	res = zeCommandListClose(commandList);
965	if (res != 0) return VKFFT_ERROR_FAILED_TO_END_COMMAND_BUFFER;
966
967	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
968	res = zeCommandQueueExecuteCommandLists(vkGPU->commandQueue, 1, &commandList, 0);
969	if (res != 0) return VKFFT_ERROR_FAILED_TO_SUBMIT_QUEUE;
970	res = zeCommandQueueSynchronize(vkGPU->commandQueue, UINT32_MAX);
971	if (res != 0) return VKFFT_ERROR_FAILED_TO_WAIT_FOR_FENCES;
972
973	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
974	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
975	time_result[0] = totTime / num_iter;
976	res = zeCommandListDestroy(commandList);
977	if (res != 0) return VKFFT_ERROR_FAILED_TO_DESTROY_COMMAND_LIST;
978#elif(VKFFT_BACKEND==5)
979	MTL::CommandBuffer* commandBuffer = vkGPU->queue->commandBuffer();
980	if (commandBuffer == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
981	launchParams->commandBuffer = commandBuffer;
982	MTL::ComputeCommandEncoder* commandEncoder = commandBuffer->computeCommandEncoder();
983	if (commandEncoder == 0) return VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_LIST;
984	launchParams->commandEncoder = commandEncoder;
985	for (uint64_t i = 0; i < num_iter; i++) {
986		resFFT = VkFFTAppend(app, -1, launchParams);
987		if (resFFT != VKFFT_SUCCESS) return resFFT;
988		resFFT = VkFFTAppend(app, 1, launchParams);
989		if (resFFT != VKFFT_SUCCESS) return resFFT;
990	}
991	commandEncoder->endEncoding();
992
993	std::chrono::steady_clock::time_point timeSubmit = std::chrono::steady_clock::now();
994	commandBuffer->commit();
995	commandBuffer->waitUntilCompleted();
996	std::chrono::steady_clock::time_point timeEnd = std::chrono::steady_clock::now();
997	double totTime = std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeSubmit).count() * 0.001;
998	time_result[0] = totTime / num_iter;
999	commandEncoder->release();
1000	commandBuffer->release();
1001#endif
1002	return resFFT;
1003}