yum-mirror/VkFFT

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

Dmitrii TolmachevSeparate complex components read/write support (https://github.com/DTolm/VkFFT/issues/149) -Real and imaginary parts of a number can now be supplied from two buffers with their own respective offsets (so both buffers can actually be just one with an offset for I). -Any buffer array pointer can be provided in such form (as two buffers) - input, output, buffer, kernel, temp. Need to set the respective bufferNum and bufferSeparateComplexComponents value. -Updated samples 0, 50 and 11 (for now) to demonstrate how this feature works. -Bugfixes.5f7df3f

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