adding Vulkan device creation, adding unit tests, adding few API function declarations

This commit is contained in:
2024-10-10 15:22:56 +02:00
parent f189928c82
commit 405c8b186a
23 changed files with 517 additions and 64 deletions

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@
!/Includes/
!/Sources/
!/Tests/
!/Examples/
!/.github/
build/
Build/

23
Examples/Vulkan/main.c git.filemode.normal_file
View File

@@ -0,0 +1,23 @@
#include <Pulse.h>
#include <stdio.h>
#define CHECK_PULSE_HANDLE_RETVAL(handle, retval) \
if(handle == PULSE_NULL_HANDLE) \
{ \
fprintf(stderr, "Error: %s", PulseVerbaliseErrorType(PulseGetLastErrorType())); \
return retval; \
} \
int main(void)
{
PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_VULKAN, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_NO_DEBUG);
CHECK_PULSE_HANDLE_RETVAL(backend, 1);
PulseDevice device = PulseCreateDevice(backend, NULL, 0);
CHECK_PULSE_HANDLE_RETVAL(device, 1);
PulseDestroyDevice(device);
PulseUnloadBackend(backend);
puts("Successfully loaded Pulse using Vulkan !");
return 0;
}

View File

@@ -1,7 +1,7 @@
target("LoadingPulse")
target("VulkanExample")
add_deps("pulse_gpu")
if is_plat("linux") then
set_extension(".x86_64")
end
add_files("main.c")
add_files("*.c")
target_end()

7
Examples/xmake.lua git.filemode.normal_file
View File

@@ -0,0 +1,7 @@
option("examples", { description = "Build the examples", default = false })
if has_config("examples") then
set_group("Examples")
includes("*/xmake.lua")
end

View File

@@ -27,9 +27,9 @@ PULSE_DEFINE_NULLABLE_HANDLE(PulseBuffer);
PULSE_DEFINE_NULLABLE_HANDLE(PulseCommandList);
PULSE_DEFINE_NULLABLE_HANDLE(PulseComputePass);
PULSE_DEFINE_NULLABLE_HANDLE(PulseComputePipeline);
PULSE_DEFINE_NULLABLE_HANDLE(PulseCopyPass);
PULSE_DEFINE_NULLABLE_HANDLE(PulseDevice);
PULSE_DEFINE_NULLABLE_HANDLE(PulseFence);
PULSE_DEFINE_NULLABLE_HANDLE(PulseGeneralPass);
PULSE_DEFINE_NULLABLE_HANDLE(PulseImage);
// Flags
@@ -83,6 +83,19 @@ typedef enum PulseDebugLevel
PULSE_PARANOID_DEBUG // Causes every warning to be treated as error
} PulseDebugLevel;
typedef enum PulseDebugMessageSeverity
{
PULSE_DEBUG_MESSAGE_SEVERITY_INFO,
PULSE_DEBUG_MESSAGE_SEVERITY_WARNING,
PULSE_DEBUG_MESSAGE_SEVERITY_ERROR
} PulseDebugMessageSeverity;
typedef enum PulseDebugMessageType
{
PULSE_DEBUG_MESSAGE_TYPE_GENERAL,
PULSE_DEBUG_MESSAGE_TYPE_PERFORMANCE
} PulseDebugMessageType;
typedef enum PulseErrorType
{
PULSE_ERROR_NONE,
@@ -250,14 +263,37 @@ typedef struct PulseImageRegion
} PulseImageRegion;
// Functions
typedef void (*PulseDebugCallbackPFN)(PulseDebugMessageSeverity, PulseDebugMessageType, const char*);
PULSE_API PulseBackend PulseLoadBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used, PulseDebugLevel debug_level);
PULSE_API void PulseUnloadBackend(PulseBackend backend);
PULSE_API void PulseSetDebugCallback(PulseBackend backend, PulseDebugCallbackPFN callback);
PULSE_API PulseDevice PulseCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count);
PULSE_API void PulseDestroyDevice(PulseDevice device);
PULSE_API PulseBackendBits PulseGetBackendInUseByDevice(PulseDevice device);
PULSE_API bool PulseSupportsBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used);
PULSE_API bool PulseDeviceSupportsShaderFormats(PulseDevice device, PulseShaderFormatsFlags shader_formats_used);
PULSE_API PulseCommandList PulseRequestCommandList(PulseDevice device);
PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence);
PULSE_API void PulseReleaseCommandList(PulseDevice device, PulseCommandList cmd);
PULSE_API PulseComputePass PulseBeginComputePass(PulseCommandList cmd);
PULSE_API void PulseEndComputePass(PulseComputePass pass);
PULSE_API PulseGeneralPass PulseBeginGeneralPass(PulseCommandList cmd);
PULSE_API void PulseEndGeneralPass(PulseGeneralPass pass);
PULSE_API PulseFence PulseCreateFence(PulseDevice device);
PULSE_API void PulseDestroyFence(PulseDevice device, PulseFence fence);
PULSE_API bool PulseIsFenceReady(PulseDevice device, PulseFence fence);
PULSE_API bool PulseWaitForFences(PulseDevice device, PulseFence* const* fences, uint32_t fences_count, bool wait_for_all);
PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info);
PULSE_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline);
PULSE_API void PulseBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline);
PULSE_API PulseErrorType PulseGetLastErrorType(); // /!\ Warning /!\ Call to this function resets the internal last error variable
PULSE_API const char* PulseVerbaliseErrorType(PulseErrorType error);

View File

@@ -12,7 +12,18 @@
#include "VulkanDevice.h"
#include "VulkanInstance.h"
#define VULKAN_RETRIEVE_DRIVER_DATA(device) ((VulkanDriverData*)device->driver_data)
#include "../../PulseInternal.h"
#define VULKAN_RETRIEVE_DRIVER_DATA(handle) ((VulkanDriverData*)handle->driver_data)
#define CHECK_VK_RETVAL(res, error, retval) \
if((res) != VK_SUCCESS) \
{ \
PulseSetInternalError(error); \
return retval; \
}
#define CHECK_VK(res, error) CHECK_VK_RETVAL(res, error, )
typedef struct VulkanGlobal
{
@@ -24,7 +35,6 @@ typedef struct VulkanGlobal
typedef struct VulkanDriverData
{
VulkanInstance instance;
VulkanDevice device;
} VulkanDriverData;
VulkanGlobal* VulkanGetGlobal();

View File

@@ -0,0 +1,24 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#ifdef PULSE_ENABLE_VULKAN_BACKEND
#ifndef PULSE_VULKAN_COMPUTE_PIPELINE_H_
#define PULSE_VULKAN_COMPUTE_PIPELINE_H_
#include <vulkan/vulkan_core.h>
#include <Pulse.h>
typedef struct VulkanComputePipeline
{
} VulkanComputePipeline;
PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info);
void VulkanBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline);
void VulkanDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline);
#endif // PULSE_VULKAN_COMPUTE_PIPELINE_H_
#endif // PULSE_ENABLE_VULKAN_BACKEND

View File

@@ -2,26 +2,32 @@
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#include "VulkanInstance.h"
#include "Pulse.h"
#include "Vulkan.h"
#include "VulkanComputePipeline.h"
#include "VulkanDevice.h"
#include "VulkanInstance.h"
#include "VulkanLoader.h"
#include "VulkanQueue.h"
#include "../../PulseInternal.h"
/*
static int32_t VulkanScorePhysicalDevice(VkPhysicalDevice device, const char** device_extensions, uint32_t device_extensions_count)
#include <string.h>
static int32_t VulkanScorePhysicalDevice(VulkanInstance* instance, VkPhysicalDevice device, const char** device_extensions, uint32_t device_extensions_count)
{
PULSE_DECLARE_STACK_FIXED_ALLOCATOR(allocator, sizeof(VkExtensionProperties) * 4096, sizeof(VkExtensionProperties));
// Check extensions support
uint32_t extension_count;
kbhGetVulkanPFNs()->vkEnumerateDeviceExtensionProperties(device, PULSE_NULLPTR, &extension_count, PULSE_NULLPTR);
VkExtensionProperties* props = (VkExtensionProperties*)kbhCallocInFixed(&allocator, extension_count, sizeof(VkExtensionProperties));
instance->vkEnumerateDeviceExtensionProperties(device, PULSE_NULLPTR, &extension_count, PULSE_NULLPTR);
VkExtensionProperties* props = (VkExtensionProperties*)calloc(extension_count, sizeof(VkExtensionProperties));
if(!props)
return -1;
kbhGetVulkanPFNs()->vkEnumerateDeviceExtensionProperties(device, PULSE_NULLPTR, &extension_count, props);
instance->vkEnumerateDeviceExtensionProperties(device, PULSE_NULLPTR, &extension_count, props);
bool are_there_required_device_extensions = true;
for(int j = 0; j < device_extensions_count; j++)
for(uint32_t j = 0; j < device_extensions_count; j++)
{
bool is_there_extension = false;
for(int k = 0; k < extension_count; k++)
for(uint32_t k = 0; k < extension_count; k++)
{
if(strcmp(device_extensions[j], props[k].extensionName) == 0)
{
@@ -40,18 +46,18 @@ static int32_t VulkanScorePhysicalDevice(VkPhysicalDevice device, const char** d
// Check Queue Families Support
int32_t queue;
if(kbhFindPhysicalDeviceQueueFamily(device, KBH_VULKAN_QUEUE_COMPUTE, &queue) != KBH_RHI_SUCCESS)
if(!VulkanFindPhysicalDeviceQueueFamily(instance, device, VULKAN_QUEUE_COMPUTE, &queue))
return -1;
VkPhysicalDeviceProperties device_props;
kbhGetVulkanPFNs()->vkGetPhysicalDeviceProperties(device, &device_props);
instance->vkGetPhysicalDeviceProperties(device, &device_props);
VkPhysicalDeviceFeatures device_features;
kbhGetVulkanPFNs()->vkGetPhysicalDeviceFeatures(device, &device_features);
instance->vkGetPhysicalDeviceFeatures(device, &device_features);
int32_t score = -1;
if(device_props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
score += 1001;
score += 10000;
score += device_props.limits.maxComputeWorkGroupCount[0];
score += device_props.limits.maxComputeWorkGroupCount[1];
@@ -61,7 +67,20 @@ static int32_t VulkanScorePhysicalDevice(VkPhysicalDevice device, const char** d
return score;
}
static VkPhysicalDevice VulkanPickPhysicalDevice(VulkanInstance* instance)
static bool VulkanIsDeviceForbidden(VkPhysicalDevice device, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count)
{
if(device == VK_NULL_HANDLE)
return true;
#pragma omp parallel for
for(uint32_t i = 0; i < forbiden_devices_count; i++)
{
if(device == ((VulkanDevice*)forbiden_devices[i]->driver_data)->physical)
return true;
}
return false;
}
static VkPhysicalDevice VulkanPickPhysicalDevice(VulkanInstance* instance, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count)
{
VkPhysicalDevice* devices = PULSE_NULLPTR;
VkPhysicalDevice chosen_one = VK_NULL_HANDLE;
@@ -73,9 +92,12 @@ static VkPhysicalDevice VulkanPickPhysicalDevice(VulkanInstance* instance)
PULSE_CHECK_ALLOCATION_RETVAL(devices, VK_NULL_HANDLE);
instance->vkEnumeratePhysicalDevices(instance->instance, &device_count, devices);
for(int i = 0; i < device_count; i++)
#pragma omp parallel for
for(uint32_t i = 0; i < device_count; i++)
{
int32_t current_device_score = VulkanScorePhysicalDevice(devices[i], PULSE_NULLPTR, 0);
if(VulkanIsDeviceForbidden(devices[i], forbiden_devices, forbiden_devices_count))
continue;
int32_t current_device_score = VulkanScorePhysicalDevice(instance, devices[i], PULSE_NULLPTR, 0);
if(current_device_score > best_device_score)
{
best_device_score = current_device_score;
@@ -84,17 +106,132 @@ static VkPhysicalDevice VulkanPickPhysicalDevice(VulkanInstance* instance)
}
return chosen_one;
}
*/
void* VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count)
PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count)
{
PULSE_CHECK_HANDLE_RETVAL(backend, PULSE_NULLPTR);
PulseDevice pulse_device = (PulseDeviceHandler*)calloc(1, sizeof(PulseDeviceHandler));
PULSE_CHECK_ALLOCATION_RETVAL(pulse_device, PULSE_NULL_HANDLE);
VulkanDevice* device = (VulkanDevice*)calloc(1, sizeof(VulkanDevice));
PULSE_CHECK_ALLOCATION_RETVAL(device, PULSE_NULLPTR);
VulkanInstance* instance = &VULKAN_RETRIEVE_DRIVER_DATA(backend)->instance;
device->physical = VulkanPickPhysicalDevice(instance, forbiden_devices, forbiden_devices_count);
PULSE_CHECK_HANDLE_RETVAL(device->physical, PULSE_NULLPTR);
const float queue_priority = 1.0f;
VkDeviceQueueCreateInfo* queue_create_infos = (VkDeviceQueueCreateInfo*)PulseStaticAllocStack(VULKAN_QUEUE_END_ENUM * sizeof(VkDeviceQueueCreateInfo));
// No need to check allocation, it is allocated on the stack
uint32_t unique_queues_count = 1;
for(int32_t i = 0; i < VULKAN_QUEUE_END_ENUM; i++) // Needs to be done before next loop
{
if(!VulkanPrepareDeviceQueue(instance, device, i))
{
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
return PULSE_NULLPTR;
}
}
for(int32_t i = 0; i < VULKAN_QUEUE_END_ENUM; i++)
{
if(device->queues[i]->queue_family_index == -1)
continue;
int j;
for(j = i; j < VULKAN_QUEUE_END_ENUM; j++) // Ugly shit but array will never be big so it's okay
{
if(device->queues[i]->queue_family_index == device->queues[j]->queue_family_index)
break;
}
if(j == VULKAN_QUEUE_END_ENUM)
unique_queues_count++;
queue_create_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_infos[i].queueFamilyIndex = device->queues[i]->queue_family_index;
queue_create_infos[i].queueCount = 1;
queue_create_infos[i].pQueuePriorities = &queue_priority;
queue_create_infos[i].flags = 0;
queue_create_infos[i].pNext = PULSE_NULLPTR;
}
instance->vkGetPhysicalDeviceProperties(device->physical, &device->properties);
instance->vkGetPhysicalDeviceMemoryProperties(device->physical, &device->memory_properties);
instance->vkGetPhysicalDeviceFeatures(device->physical, &device->features);
VkDeviceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.queueCreateInfoCount = unique_queues_count;
create_info.pQueueCreateInfos = queue_create_infos;
create_info.pEnabledFeatures = &device->features;
create_info.enabledExtensionCount = 0;
create_info.ppEnabledExtensionNames = PULSE_NULLPTR;
create_info.enabledLayerCount = 0;
create_info.ppEnabledLayerNames = PULSE_NULLPTR;
create_info.flags = 0;
create_info.pNext = PULSE_NULLPTR;
CHECK_VK_RETVAL(instance->vkCreateDevice(device->physical, &create_info, PULSE_NULLPTR, &device->device), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULLPTR);
if(!VulkanLoadDevice(instance, device))
{
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
return PULSE_NULLPTR;
}
for(int32_t i = 0; i < VULKAN_QUEUE_END_ENUM; i++)
{
if(!VulkanRetrieveDeviceQueue(device, (VulkanQueueType)i))
{
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
return PULSE_NULLPTR;
}
}
VmaVulkanFunctions vma_vulkan_func = {};
vma_vulkan_func.vkAllocateMemory = device->vkAllocateMemory;
vma_vulkan_func.vkBindBufferMemory = device->vkBindBufferMemory;
vma_vulkan_func.vkBindImageMemory = device->vkBindImageMemory;
vma_vulkan_func.vkCreateBuffer = device->vkCreateBuffer;
vma_vulkan_func.vkCreateImage = device->vkCreateImage;
vma_vulkan_func.vkDestroyBuffer = device->vkDestroyBuffer;
vma_vulkan_func.vkDestroyImage = device->vkDestroyImage;
vma_vulkan_func.vkFlushMappedMemoryRanges = device->vkFlushMappedMemoryRanges;
vma_vulkan_func.vkFreeMemory = device->vkFreeMemory;
vma_vulkan_func.vkGetBufferMemoryRequirements = device->vkGetBufferMemoryRequirements;
vma_vulkan_func.vkGetImageMemoryRequirements = device->vkGetImageMemoryRequirements;
vma_vulkan_func.vkInvalidateMappedMemoryRanges = device->vkInvalidateMappedMemoryRanges;
vma_vulkan_func.vkMapMemory = device->vkMapMemory;
vma_vulkan_func.vkUnmapMemory = device->vkUnmapMemory;
vma_vulkan_func.vkCmdCopyBuffer = device->vkCmdCopyBuffer;
vma_vulkan_func.vkGetPhysicalDeviceMemoryProperties = instance->vkGetPhysicalDeviceMemoryProperties;
vma_vulkan_func.vkGetPhysicalDeviceProperties = instance->vkGetPhysicalDeviceProperties;
VmaAllocatorCreateInfo allocator_create_info = {};
allocator_create_info.vulkanApiVersion = VK_API_VERSION_1_0;
allocator_create_info.physicalDevice = device->physical;
allocator_create_info.device = device->device;
allocator_create_info.instance = instance->instance;
allocator_create_info.pVulkanFunctions = &vma_vulkan_func;
CHECK_VK_RETVAL(vmaCreateAllocator(&allocator_create_info, &device->allocator), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULLPTR);
pulse_device->driver_data = device;
pulse_device->backend = backend;
PULSE_LOAD_DRIVER_DEVICE(Vulkan);
return pulse_device;
}
void VulkanDestroyDevice(VulkanDevice* device)
void VulkanDestroyDevice(PulseDevice device)
{
if(device == PULSE_NULLPTR || device->device == VK_NULL_HANDLE)
VulkanDevice* vulkan_device = (VulkanDevice*)device->driver_data;
if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE)
return;
vmaDestroyAllocator(device->allocator);
device->vkDestroyDevice(device->device, PULSE_NULLPTR);
device->device = VK_NULL_HANDLE;
vmaDestroyAllocator(vulkan_device->allocator);
vulkan_device->vkDestroyDevice(vulkan_device->device, PULSE_NULLPTR);
vulkan_device->device = VK_NULL_HANDLE;
free(vulkan_device);
free(device);
}

View File

@@ -16,8 +16,14 @@
#include <Pulse.h>
#include "VulkanEnums.h"
struct VulkanQueue;
typedef struct VulkanDevice
{
struct VulkanQueue* queues[VULKAN_QUEUE_END_ENUM];
VkPhysicalDeviceFeatures features;
VkPhysicalDeviceMemoryProperties memory_properties;
VkPhysicalDeviceProperties properties;
@@ -32,8 +38,8 @@ typedef struct VulkanDevice
#undef PULSE_VULKAN_DEVICE_FUNCTION
} VulkanDevice;
void* VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count);
void VulkanDestroyDevice(VulkanDevice* device);
PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count);
void VulkanDestroyDevice(PulseDevice device);
#endif // PULSE_VULKAN_DEVICE_H_

20
Sources/Backends/Vulkan/VulkanEnums.h git.filemode.normal_file
View File

@@ -0,0 +1,20 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#ifdef PULSE_ENABLE_VULKAN_BACKEND
#ifndef PULSE_VULKAN_ENUMS_H_
#define PULSE_VULKAN_ENUMS_H_
typedef enum VulkanQueueType
{
VULKAN_QUEUE_COMPUTE = 0,
VULKAN_QUEUE_TRANSFER = 1,
VULKAN_QUEUE_END_ENUM // For internal use only
} VulkanQueueType;
#endif // PULSE_VULKAN_ENUMS_H_
#endif // PULSE_ENABLE_VULKAN_BACKEND

View File

@@ -36,7 +36,7 @@ static VkInstance VulkanCreateInstance(const char** extensions_enabled, uint32_t
create_info.flags = 0;
#endif
VulkanGetGlobal()->vkCreateInstance(&create_info, PULSE_NULLPTR, &instance);
CHECK_VK_RETVAL(VulkanGetGlobal()->vkCreateInstance(&create_info, PULSE_NULLPTR, &instance), PULSE_ERROR_INITIALIZATION_FAILED, VK_NULL_HANDLE);
return instance;
}
@@ -52,10 +52,7 @@ bool VulkanInitInstance(VulkanInstance* instance, PulseDebugLevel debug_level)
#endif
instance->instance = VulkanCreateInstance(extensions, sizeof(extensions) / sizeof(char*), debug_level);
if(instance->instance == VK_NULL_HANDLE)
{
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
return false;
}
if(!VulkanLoadInstance(instance))
return false;
return true;

View File

@@ -2,5 +2,8 @@
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#define VMA_VULKAN_VERSION 1000000
#define VMA_IMPLEMENTATION
#include <vk_mem_alloc.h>

83
Sources/Backends/Vulkan/VulkanQueue.c git.filemode.normal_file
View File

@@ -0,0 +1,83 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#include "VulkanQueue.h"
#include "Vulkan.h"
#include <stdlib.h>
bool VulkanFindPhysicalDeviceQueueFamily(VulkanInstance* instance, VkPhysicalDevice physical, VulkanQueueType type, int32_t* queue_family_index)
{
if(physical == VK_NULL_HANDLE)
return false;
uint32_t queue_family_count;
instance->vkGetPhysicalDeviceQueueFamilyProperties(physical, &queue_family_count, PULSE_NULLPTR);
VkQueueFamilyProperties* queue_families = (VkQueueFamilyProperties*)calloc(queue_family_count, sizeof(VkQueueFamilyProperties));
if(!queue_families)
return false;
instance->vkGetPhysicalDeviceQueueFamilyProperties(physical, &queue_family_count, queue_families);
bool found = false;
for(uint32_t i = 0; i < queue_family_count; i++)
{
if(type == VULKAN_QUEUE_COMPUTE)
{
// try to find a queue that's only for compute
if(queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT && (queue_families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) == 0)
{
*queue_family_index = i;
found = true;
break;
}
if(queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) // else just find a compute queue
{
*queue_family_index = i;
found = true;
break;
}
}
else if(type == VULKAN_QUEUE_TRANSFER)
{
if(queue_families[i].queueFlags & VK_QUEUE_TRANSFER_BIT && (queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) == 0)
{
*queue_family_index = i;
found = true;
break;
}
if(queue_families[i].queueFlags & VK_QUEUE_TRANSFER_BIT)
{
*queue_family_index = i;
found = true;
break;
}
}
}
free(queue_families);
return found;
}
bool VulkanPrepareDeviceQueue(VulkanInstance* instance, VulkanDevice* device, VulkanQueueType type)
{
if(device == PULSE_NULLPTR)
return false;
device->queues[(int)type] = (VulkanQueue*)malloc(sizeof(VulkanQueue));
if(!device->queues[(int)type])
return false;
VulkanQueue* queue = device->queues[(int)type];
if(!queue)
return false;
return VulkanFindPhysicalDeviceQueueFamily(instance, device->physical, type, &queue->queue_family_index);
}
bool VulkanRetrieveDeviceQueue(VulkanDevice* device, VulkanQueueType type)
{
if(device == PULSE_NULLPTR)
return false;
VulkanQueue* queue = device->queues[(int)type];
if(!queue)
return false;
device->vkGetDeviceQueue(device->device, queue->queue_family_index, 0, &queue->queue);
return true;
}

27
Sources/Backends/Vulkan/VulkanQueue.h git.filemode.normal_file
View File

@@ -0,0 +1,27 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#ifdef PULSE_ENABLE_VULKAN_BACKEND
#ifndef PULSE_VULKAN_QUEUES_H_
#define PULSE_VULKAN_QUEUES_H_
#include "VulkanEnums.h"
#include "VulkanDevice.h"
#include "VulkanInstance.h"
typedef struct VulkanQueue
{
VulkanDevice* device;
VkQueue queue;
int32_t queue_family_index;
} VulkanQueue;
bool VulkanFindPhysicalDeviceQueueFamily(VulkanInstance* instance, VkPhysicalDevice physical, VulkanQueueType type, int32_t* queue_family_index);
bool VulkanPrepareDeviceQueue(VulkanInstance* instance, VulkanDevice* device, VulkanQueueType type);
bool VulkanRetrieveDeviceQueue(VulkanDevice* device, VulkanQueueType type);
#endif // PULSE_VULKAN_QUEUES_H_
#endif // PULSE_ENABLE_VULKAN_BACKEND

22
Sources/PulseComputePipeline.c git.filemode.normal_file
View File

@@ -0,0 +1,22 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#include <Pulse.h>
#include "PulseInternal.h"
PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info)
{
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE);
}
PULSE_API void PulseBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline)
{
PULSE_CHECK_HANDLE(pass);
PULSE_CHECK_HANDLE(pipeline);
}
PULSE_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline)
{
PULSE_CHECK_HANDLE(device);
}

View File

@@ -7,17 +7,15 @@
PULSE_API PulseDevice PulseCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count)
{
PulseDevice device = backend->PFN_CreateDevice(backend, forbiden_devices, forbiden_devices_count);
if(device == PULSE_NULL_HANDLE)
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
device->backend = backend;
return device;
PULSE_CHECK_HANDLE_RETVAL(backend, PULSE_NULL_HANDLE);
return backend->PFN_CreateDevice(backend, forbiden_devices, forbiden_devices_count);
}
PULSE_API void PulseDestroyDevice(PulseDevice device)
{
PULSE_CHECK_HANDLE(device);
device->PFN_DestroyDevice(device);
device->driver_data = PULSE_NULLPTR;
}
PULSE_API PulseBackendBits PulseGetBackendInUseByDevice(PulseDevice device)

View File

@@ -11,6 +11,8 @@
extern "C" {
#endif
#define PulseStaticAllocStack(size) ((char[size]){ 0 })
#define PULSE_CHECK_ALLOCATION_RETVAL(ptr, retval) \
do { \
if(ptr == PULSE_NULLPTR) \
@@ -37,9 +39,12 @@ typedef PulseBackendFlags (*PulseCheckBackendSupportPFN)(PulseBackendFlags, Puls
typedef bool (*PulseLoadBackendPFN)(PulseDebugLevel);
typedef void (*PulseUnloadBackendPFN)(PulseBackend);
typedef void* (*PulseCreateDevicePFN)(PulseBackend, PulseDevice*, uint32_t);
typedef PulseDevice (*PulseCreateDevicePFN)(PulseBackend, PulseDevice*, uint32_t);
typedef void (*PulseDestroyDevicePFN)(PulseDevice);
typedef PulseComputePipeline (*PulseCreateComputePipelinePFN)(PulseDevice, const PulseComputePipelineCreateInfo*);
typedef void (*PulseBindComputePipelinePFN)(PulseComputePass, PulseComputePipeline);
typedef void (*PulseDestroyComputePipelinePFN)(PulseDevice, PulseComputePipeline);
typedef struct PulseBackendHandler
{
@@ -58,6 +63,9 @@ typedef struct PulseDeviceHandler
{
// PFNs
PulseDestroyDevicePFN PFN_DestroyDevice;
PulseCreateComputePipelinePFN PFN_CreateComputePipeline;
PulseBindComputePipelinePFN PFN_BindComputePipeline;
PulseDestroyComputePipelinePFN PFN_DestroyComputePipeline;
// Attributes
void* driver_data;
@@ -66,9 +74,12 @@ typedef struct PulseDeviceHandler
void PulseSetInternalError(PulseErrorType error);
#define PULSE_LOAD_DRIVER_DEVICE_FUNCTION(fn, _namespace) device->PFN_##fn = _namespace##fn;
#define PULSE_LOAD_DRIVER_DEVICE_FUNCTION(fn, _namespace) pulse_device->PFN_##fn = _namespace##fn;
#define PULSE_LOAD_DRIVER_DEVICE(_namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateComputePipeline, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(BindComputePipeline, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyComputePipeline, _namespace) \
#ifdef PULSE_ENABLE_VULKAN_BACKEND
extern PulseBackendHandler VulkanDriver;

View File

@@ -1,16 +0,0 @@
#include <Pulse.h>
#include <stdio.h>
int main(void)
{
PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_ANY, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_NO_DEBUG);
if(backend == PULSE_NULL_HANDLE)
{
fprintf(stderr, "Error while loading Pulse: %s", PulseVerbaliseErrorType(PulseGetLastErrorType()));
return 1;
}
PulseUnloadBackend(backend);
puts("Successfully loaded Pulse !");
return 0;
}

11
Tests/Vulkan/DeviceSetup.c git.filemode.normal_file
View File

@@ -0,0 +1,11 @@
#include <unity/unity.h>
#include <Pulse.h>
extern PulseBackend backend;
void TestDeviceSetup()
{
PulseDevice device = PulseCreateDevice(backend, NULL, 0);
TEST_ASSERT_NOT_EQUAL_MESSAGE(device, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
PulseDestroyDevice(device);
}

41
Tests/Vulkan/main.c git.filemode.normal_file
View File

@@ -0,0 +1,41 @@
#include <unity/unity.h>
#include <Pulse.h>
#include <stdbool.h>
PulseBackend backend;
extern void TestDeviceSetup();
bool SetupPulse()
{
backend = PulseLoadBackend(PULSE_BACKEND_VULKAN, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_NO_DEBUG);
if(backend == PULSE_NULL_HANDLE)
{
fprintf(stderr, "Fatal Error: could not load Pulse using Vulkan due to %s", PulseVerbaliseErrorType(PulseGetLastErrorType()));
return false;
}
puts("Pulse loaded using Vulkan");
return true;
}
int RunUnitTests()
{
UNITY_BEGIN();
RUN_TEST(TestDeviceSetup);
return UNITY_END();
}
void UnloadPulse()
{
PulseUnloadBackend(backend);
puts("Pulse unloaded");
}
int main(void)
{
SetupPulse();
int result = RunUnitTests();
UnloadPulse();
return result;
}

16
Tests/Vulkan/xmake.lua git.filemode.normal_file
View File

@@ -0,0 +1,16 @@
option("vulkan-tests", { description = "Build Vulkan tests", default = false })
if has_config("vulkan-tests") then
set_group("VulkanTests")
add_requires("unity_test")
target("VulkanUnitTests")
set_kind("binary")
add_deps("pulse_gpu")
add_files("**.c")
add_packages("unity_test")
if is_plat("linux") then
set_extension(".x86_64")
end
target_end()
end

View File

@@ -1,6 +0,0 @@
option("tests", { description = "Build tests", default = false })
if has_config("tests") then
set_group("Tests")
includes("*/xmake.lua")
end

View File

@@ -97,4 +97,6 @@ target("pulse_gpu")
end)
target_end()
includes("Tests/*.lua")
includes("Examples/*.lua")
includes("Tests/Vulkan/*.lua")