adding command list Vulkan support

This commit is contained in:
2024-11-17 10:52:29 +01:00
parent 0b417483f3
commit d8b14d1a7e
29 changed files with 716 additions and 186 deletions

View File

@@ -3,29 +3,47 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#define CHECK_PULSE_HANDLE_RETVAL(handle, retval) \
if(handle == PULSE_NULL_HANDLE) \
{ \
fprintf(stderr, "Error: %s", PulseVerbaliseErrorType(PulseGetLastErrorType())); \
return retval; \
} \
void DebugCallBack(PulseDebugMessageSeverity severity, const char* message) void DebugCallBack(PulseDebugMessageSeverity severity, const char* message)
{ {
if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_ERROR) if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_ERROR)
{ {
fprintf(stderr, "Pulse Error: %s", message); fprintf(stderr, "Pulse Error: %s\n", message);
exit(1); exit(1);
} }
else if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_WARNING) else if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_WARNING)
fprintf(stderr, "Pulse Warning: %s", message); fprintf(stderr, "Pulse Warning: %s\n", message);
else else
printf("Pulse: %s", message); printf("Pulse: %s\n", message);
} }
int main(void) int main(void)
{ {
PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_VULKAN, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_NO_DEBUG); PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_VULKAN, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_HIGH_DEBUG);
if(backend == PULSE_NULL_HANDLE) CHECK_PULSE_HANDLE_RETVAL(backend, 1);
{
fprintf(stderr, "Pulse: could not create backend");
return 1;
}
PulseSetDebugCallback(backend, DebugCallBack); PulseSetDebugCallback(backend, DebugCallBack);
PulseDevice device = PulseCreateDevice(backend, NULL, 0); PulseDevice device = PulseCreateDevice(backend, NULL, 0);
CHECK_PULSE_HANDLE_RETVAL(device, 1);
PulseFence fence = PulseCreateFence(device);
CHECK_PULSE_HANDLE_RETVAL(fence, 1);
PulseCommandList cmd = PulseRequestCommandList(device, PULSE_COMMAND_LIST_GENERAL);
CHECK_PULSE_HANDLE_RETVAL(cmd, 1);
if(!PulseSubmitCommandList(device, cmd, fence))
fprintf(stderr, "Could not submit command list, %s\n", PulseVerbaliseErrorType(PulseGetLastErrorType()));
if(!PulseWaitForFences(device, &fence, 1, true))
fprintf(stderr, "Could not wait for fences, %s\n", PulseVerbaliseErrorType(PulseGetLastErrorType()));
PulseReleaseCommandList(device, cmd);
PulseDestroyFence(device, fence);
PulseDestroyDevice(device); PulseDestroyDevice(device);
PulseUnloadBackend(backend); PulseUnloadBackend(backend);

View File

@@ -25,11 +25,9 @@ typedef uint32_t PulseFlags;
PULSE_DEFINE_NULLABLE_HANDLE(PulseBackend); PULSE_DEFINE_NULLABLE_HANDLE(PulseBackend);
PULSE_DEFINE_NULLABLE_HANDLE(PulseBuffer); PULSE_DEFINE_NULLABLE_HANDLE(PulseBuffer);
PULSE_DEFINE_NULLABLE_HANDLE(PulseCommandList); PULSE_DEFINE_NULLABLE_HANDLE(PulseCommandList);
PULSE_DEFINE_NULLABLE_HANDLE(PulseComputePass);
PULSE_DEFINE_NULLABLE_HANDLE(PulseComputePipeline); PULSE_DEFINE_NULLABLE_HANDLE(PulseComputePipeline);
PULSE_DEFINE_NULLABLE_HANDLE(PulseDevice); PULSE_DEFINE_NULLABLE_HANDLE(PulseDevice);
PULSE_DEFINE_NULLABLE_HANDLE(PulseFence); PULSE_DEFINE_NULLABLE_HANDLE(PulseFence);
PULSE_DEFINE_NULLABLE_HANDLE(PulseGeneralPass);
PULSE_DEFINE_NULLABLE_HANDLE(PulseImage); PULSE_DEFINE_NULLABLE_HANDLE(PulseImage);
// Flags // Flags
@@ -75,12 +73,18 @@ typedef enum PulseShaderFormatsBits
typedef PulseFlags PulseShaderFormatsFlags; typedef PulseFlags PulseShaderFormatsFlags;
// Enums // Enums
typedef enum PulseCommandListUsage
{
PULSE_COMMAND_LIST_GENERAL,
PULSE_COMMAND_LIST_TRANSFER_ONLY
} PulseCommandListUsage;
typedef enum PulseDebugLevel typedef enum PulseDebugLevel
{ {
PULSE_NO_DEBUG, PULSE_NO_DEBUG = 0,
PULSE_LOW_DEBUG, PULSE_LOW_DEBUG = 1,
PULSE_HIGH_DEBUG, PULSE_HIGH_DEBUG = 2,
PULSE_PARANOID_DEBUG // Causes every warning to be treated as error PULSE_PARANOID_DEBUG = 3 // Causes every warning to be treated as error
} PulseDebugLevel; } PulseDebugLevel;
typedef enum PulseDebugMessageSeverity typedef enum PulseDebugMessageSeverity
@@ -90,6 +94,18 @@ typedef enum PulseDebugMessageSeverity
PULSE_DEBUG_MESSAGE_SEVERITY_ERROR PULSE_DEBUG_MESSAGE_SEVERITY_ERROR
} PulseDebugMessageSeverity; } PulseDebugMessageSeverity;
typedef enum PulseErrorType
{
PULSE_ERROR_NONE,
PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH,
PULSE_ERROR_INITIALIZATION_FAILED,
PULSE_ERROR_INVALID_HANDLE,
PULSE_ERROR_CPU_ALLOCATION_FAILED,
PULSE_ERROR_DEVICE_ALLOCATION_FAILED,
PULSE_ERROR_DEVICE_LOST,
PULSE_ERROR_INVALID_INTERNAL_POINTER,
} PulseErrorType;
typedef enum PulseImageType typedef enum PulseImageType
{ {
PULSE_IMAGE_TYPE_2D, PULSE_IMAGE_TYPE_2D,
@@ -249,26 +265,22 @@ typedef struct PulseImageRegion
// Functions // Functions
typedef void (*PulseDebugCallbackPFN)(PulseDebugMessageSeverity, const char*); typedef void (*PulseDebugCallbackPFN)(PulseDebugMessageSeverity, const char*);
PULSE_API bool PulseSupportsBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used);
PULSE_API PulseBackend PulseLoadBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used, PulseDebugLevel debug_level); PULSE_API PulseBackend PulseLoadBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used, PulseDebugLevel debug_level);
PULSE_API void PulseUnloadBackend(PulseBackend backend); PULSE_API void PulseUnloadBackend(PulseBackend backend);
PULSE_API PulseBackendFlags PulseGetBackendType(PulseBackend backend);
PULSE_API void PulseSetDebugCallback(PulseBackend backend, PulseDebugCallbackPFN callback); PULSE_API void PulseSetDebugCallback(PulseBackend backend, PulseDebugCallbackPFN callback);
PULSE_API PulseDevice PulseCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count); PULSE_API PulseDevice PulseCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count);
PULSE_API void PulseDestroyDevice(PulseDevice device); PULSE_API void PulseDestroyDevice(PulseDevice device);
PULSE_API PulseBackendBits PulseGetBackendInUseByDevice(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 bool PulseDeviceSupportsShaderFormats(PulseDevice device, PulseShaderFormatsFlags shader_formats_used);
PULSE_API PulseCommandList PulseRequestCommandList(PulseDevice device); PULSE_API PulseCommandList PulseRequestCommandList(PulseDevice device, PulseCommandListUsage usage);
PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence); PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence);
PULSE_API void PulseReleaseCommandList(PulseDevice device, PulseCommandList cmd); 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 PulseFence PulseCreateFence(PulseDevice device);
PULSE_API void PulseDestroyFence(PulseDevice device, PulseFence fence); PULSE_API void PulseDestroyFence(PulseDevice device, PulseFence fence);
PULSE_API bool PulseIsFenceReady(PulseDevice device, PulseFence fence); PULSE_API bool PulseIsFenceReady(PulseDevice device, PulseFence fence);
@@ -276,7 +288,10 @@ PULSE_API bool PulseWaitForFences(PulseDevice device, const PulseFence* fences,
PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info); PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info);
PULSE_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline); PULSE_API void PulseDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline);
PULSE_API void PulseBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline); PULSE_API void PulseBindComputePipeline(PulseComputePipeline pipeline);
PULSE_API PulseErrorType PulseGetLastErrorType(); // /!\ Warning /!\ Call to this function resets the internal last error variable
PULSE_API const char* PulseVerbaliseErrorType(PulseErrorType error);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -55,6 +55,39 @@ void VulkanUnloadBackend(PulseBackend backend)
VulkanLoaderShutdown(); VulkanLoaderShutdown();
} }
const char* VulkanVerbaliseResult(VkResult res)
{
switch(res)
{
case VK_SUCCESS: return "Success";
case VK_NOT_READY: return "A fence or query has not yet completed";
case VK_TIMEOUT: return "A wait operation has not completed in the specified time";
case VK_EVENT_SET: return "An event is signaled";
case VK_EVENT_RESET: return "An event is unsignaled";
case VK_INCOMPLETE: return "A return array was too small for the result";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "A host memory allocation has failed";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "A device memory allocation has failed";
case VK_ERROR_INITIALIZATION_FAILED: return "Initialization of an object could not be completed for implementation-specific reasons";
case VK_ERROR_DEVICE_LOST: return "The logical or physical device has been lost";
case VK_ERROR_MEMORY_MAP_FAILED: return "Mapping of a memory object has failed";
case VK_ERROR_LAYER_NOT_PRESENT: return "A requested layer is not present or could not be loaded";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "A requested extension is not supported";
case VK_ERROR_FEATURE_NOT_PRESENT: return "A requested feature is not supported";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible";
case VK_ERROR_TOO_MANY_OBJECTS: return "Too many objects of the type have already been created";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "A requested format is not supported on this device";
case VK_ERROR_SURFACE_LOST_KHR: return "A surface is no longer available";
case VK_SUBOPTIMAL_KHR: return "A swapchain no longer matches the surface properties exactly, but can still be used";
case VK_ERROR_OUT_OF_DATE_KHR: return "A surface has changed in such a way that it is no longer compatible with the swapchain";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "The display used by a swapchain does not use the same presentable image layout";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API";
case VK_ERROR_VALIDATION_FAILED_EXT: return "A validation layer found an error";
default: return "Unknown Vulkan error";
}
return "Unknown Vulkan error"; // Just to avoid warnings
}
PulseBackendHandler VulkanDriver = { PulseBackendHandler VulkanDriver = {
.PFN_LoadBackend = VulkanLoadBackend, .PFN_LoadBackend = VulkanLoadBackend,
.PFN_UnloadBackend = VulkanUnloadBackend, .PFN_UnloadBackend = VulkanUnloadBackend,

View File

@@ -16,14 +16,16 @@
#define VULKAN_RETRIEVE_DRIVER_DATA_AS(handle, cast) ((cast)handle->driver_data) #define VULKAN_RETRIEVE_DRIVER_DATA_AS(handle, cast) ((cast)handle->driver_data)
#define CHECK_VK_RETVAL(res, error, retval) \ #define CHECK_VK_RETVAL(backend, res, error, retval) \
if((res) != VK_SUCCESS) \ if((res) != VK_SUCCESS) \
{ \ { \
if(backend != PULSE_NULL_HANDLE && PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(backend)) \
PulseLogErrorFmt(backend, "(Vulkan) Call to Vulkan function failed due to %s", VulkanVerbaliseResult(res)); \
PulseSetInternalError(error); \ PulseSetInternalError(error); \
return retval; \ return retval; \
} }
#define CHECK_VK(res, error) CHECK_VK_RETVAL(res, error, ) #define CHECK_VK(backend, res, error) CHECK_VK_RETVAL(backend, res, error, )
typedef struct VulkanGlobal typedef struct VulkanGlobal
{ {
@@ -39,6 +41,8 @@ typedef struct VulkanDriverData
VulkanGlobal* VulkanGetGlobal(); VulkanGlobal* VulkanGetGlobal();
const char* VulkanVerbaliseResult(VkResult res);
PulseBackendFlags VulkanCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used); // Return PULSE_BACKEND_VULKAN in case of success and PULSE_BACKEND_INVALID otherwise PulseBackendFlags VulkanCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used); // Return PULSE_BACKEND_VULKAN in case of success and PULSE_BACKEND_INVALID otherwise
#endif // PULSE_VULKAN_H_ #endif // PULSE_VULKAN_H_

171
Sources/Backends/Vulkan/VulkanCommandList.c git.filemode.normal_file
View File

@@ -0,0 +1,171 @@
// 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 "Vulkan.h"
#include "VulkanCommandList.h"
#include "VulkanCommandPool.h"
#include "VulkanDevice.h"
#include "VulkanQueue.h"
static void VulkanInitCommandList(VulkanCommandPool* pool, PulseCommandList cmd)
{
PULSE_CHECK_PTR(pool);
PULSE_CHECK_PTR(cmd);
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(pool->device, VulkanDevice*);
VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd, VulkanCommandList*);
vulkan_cmd->pool = pool;
VkCommandBufferAllocateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
info.commandPool = pool->pool;
info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
info.commandBufferCount = 1;
CHECK_VK(pool->device->backend, vulkan_device->vkAllocateCommandBuffers(vulkan_device->device, &info, &vulkan_cmd->cmd), PULSE_ERROR_INITIALIZATION_FAILED);
if(pool->available_command_lists_size == pool->available_command_lists_capacity)
{
pool->available_command_lists_capacity += 5;
pool->available_command_lists = (PulseCommandList*)realloc(pool->available_command_lists, pool->available_command_lists_capacity * sizeof(PulseCommandList));
PULSE_CHECK_ALLOCATION(pool->available_command_lists);
}
pool->available_command_lists[pool->available_command_lists_size] = cmd;
pool->available_command_lists_size++;
}
PulseCommandList VulkanRequestCommandList(PulseDevice device, PulseCommandListUsage usage)
{
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE);
VulkanCommandPool* pool;
switch(usage)
{
case PULSE_COMMAND_LIST_TRANSFER_ONLY: pool = VulkanRequestCmdPoolFromDevice(device, VULKAN_QUEUE_TRANSFER); break;
case PULSE_COMMAND_LIST_GENERAL: // fallthrough
default: pool = VulkanRequestCmdPoolFromDevice(device, VULKAN_QUEUE_COMPUTE); break;
}
PULSE_CHECK_PTR_RETVAL(pool, PULSE_NULL_HANDLE);
PulseCommandList cmd = PULSE_NULL_HANDLE;
for(uint32_t i = 0; i < pool->available_command_lists_size; i++)
{
if(pool->available_command_lists[i]->is_available)
{
cmd = pool->available_command_lists[i];
break;
}
}
if(cmd == PULSE_NULL_HANDLE)
{
cmd = (PulseCommandList)calloc(1, sizeof(PulseCommandListHandler));
PULSE_CHECK_ALLOCATION_RETVAL(cmd, PULSE_NULL_HANDLE);
VulkanCommandList* vulkan_cmd = (VulkanCommandList*)calloc(1, sizeof(VulkanCommandList));
PULSE_CHECK_ALLOCATION_RETVAL(vulkan_cmd, PULSE_NULL_HANDLE);
cmd->usage = usage;
cmd->device = device;
cmd->driver_data = vulkan_cmd;
cmd->thread_id = pool->thread_id;
VulkanInitCommandList(pool, cmd);
}
cmd->compute_pipelines_bound_size = 0;
cmd->state = PULSE_COMMAND_LIST_STATE_EMPTY;
cmd->is_available = false;
cmd->is_compute_pipeline_bound = false;
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd, VulkanCommandList*);
CHECK_VK_RETVAL(device->backend, vulkan_device->vkResetCommandBuffer(vulkan_cmd->cmd, 0), PULSE_ERROR_DEVICE_ALLOCATION_FAILED, PULSE_NULL_HANDLE);
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags = 0;
VkResult res = vulkan_device->vkBeginCommandBuffer(vulkan_cmd->cmd, &begin_info);
switch(res)
{
case VK_SUCCESS: break;
case VK_ERROR_OUT_OF_HOST_MEMORY: PulseSetInternalError(PULSE_ERROR_CPU_ALLOCATION_FAILED); return PULSE_NULL_HANDLE;
case VK_ERROR_OUT_OF_DEVICE_MEMORY: PulseSetInternalError(PULSE_ERROR_DEVICE_ALLOCATION_FAILED); return PULSE_NULL_HANDLE;
default: break;
}
return cmd;
}
bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence)
{
PULSE_UNUSED(device);
PULSE_CHECK_HANDLE_RETVAL(cmd, false);
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd, VulkanCommandList*);
VkResult res = vulkan_device->vkEndCommandBuffer(vulkan_cmd->cmd);
switch(res)
{
case VK_SUCCESS: break;
case VK_ERROR_OUT_OF_HOST_MEMORY: PulseSetInternalError(PULSE_ERROR_CPU_ALLOCATION_FAILED); return false;
case VK_ERROR_OUT_OF_DEVICE_MEMORY: PulseSetInternalError(PULSE_ERROR_DEVICE_ALLOCATION_FAILED); return false;
default: break;
}
VkFence vulkan_fence;
if(fence != PULSE_NULL_HANDLE)
{
vulkan_fence = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VkFence);
vulkan_device->vkResetFences(vulkan_device->device, 1, &vulkan_fence);
}
VulkanQueue* vulkan_queue;
switch(cmd->usage)
{
case PULSE_COMMAND_LIST_TRANSFER_ONLY: vulkan_queue = vulkan_device->queues[VULKAN_QUEUE_TRANSFER]; break;
case PULSE_COMMAND_LIST_GENERAL: // fallthrough
default: vulkan_queue = vulkan_device->queues[VULKAN_QUEUE_COMPUTE]; break;
}
PULSE_CHECK_PTR_RETVAL(vulkan_queue, false);
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &vulkan_cmd->cmd;
res = vulkan_device->vkQueueSubmit(vulkan_queue->queue, 1, &submit_info, vulkan_fence);
switch(res)
{
case VK_SUCCESS: return true;
case VK_ERROR_OUT_OF_HOST_MEMORY: PulseSetInternalError(PULSE_ERROR_CPU_ALLOCATION_FAILED); return false;
case VK_ERROR_OUT_OF_DEVICE_MEMORY: PulseSetInternalError(PULSE_ERROR_DEVICE_ALLOCATION_FAILED); return false;
case VK_ERROR_DEVICE_LOST: PulseSetInternalError(PULSE_ERROR_DEVICE_LOST); return false;
default: return false;
}
return false;
}
void VulkanReleaseCommandList(PulseDevice device, PulseCommandList cmd)
{
PULSE_CHECK_HANDLE(device);
VulkanCommandList* vulkan_cmd = VULKAN_RETRIEVE_DRIVER_DATA_AS(cmd, VulkanCommandList*);
for(uint32_t i = 0; i < vulkan_cmd->pool->available_command_lists_size; i++)
{
if(vulkan_cmd->pool->available_command_lists[i] == cmd)
{
cmd->is_available = true;
cmd->state = PULSE_COMMAND_LIST_STATE_INVALID;
break;
}
}
}

View File

@@ -10,22 +10,17 @@
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
#include <Pulse.h> #include <Pulse.h>
#include "../../PulseInternal.h"
#include "VulkanCommandPool.h" #include "VulkanCommandPool.h"
typedef struct VulkanCommandList typedef struct VulkanCommandList
{ {
PulseDevice device;
VulkanCommandPool* pool; VulkanCommandPool* pool;
PulseThreadID thread_id;
VkCommandBuffer cmd; VkCommandBuffer cmd;
PulseComputePipeline* compute_pipelines_bound;
uint32_t compute_pipelines_bound_capacity;
uint32_t compute_pipelines_bound_size;
} VulkanCommandList; } VulkanCommandList;
void VulkanInitCommandList(VulkanCommandPool* pool); PulseCommandList VulkanRequestCommandList(PulseDevice device, PulseCommandListUsage usage);
bool VulkanSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence);
void VulkanReleaseCommandList(PulseDevice device, PulseCommandList cmd);
#endif // PULSE_VULKAN_COMMAND_LIST_H_ #endif // PULSE_VULKAN_COMMAND_LIST_H_

View File

@@ -19,7 +19,7 @@ bool VulkanInitCommandPool(PulseDevice device, VulkanCommandPool* pool, VulkanQu
create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
create_info.queueFamilyIndex = vulkan_device->queues[queue_type]->queue_family_index; create_info.queueFamilyIndex = vulkan_device->queues[queue_type]->queue_family_index;
create_info.pNext = PULSE_NULLPTR; create_info.pNext = PULSE_NULLPTR;
CHECK_VK_RETVAL(vulkan_device->vkCreateCommandPool(vulkan_device->device, &create_info, PULSE_NULLPTR, &pool->pool), PULSE_ERROR_INITIALIZATION_FAILED, false); CHECK_VK_RETVAL(device->backend, vulkan_device->vkCreateCommandPool(vulkan_device->device, &create_info, PULSE_NULLPTR, &pool->pool), PULSE_ERROR_INITIALIZATION_FAILED, false);
pool->thread_id = PulseGetThreadID(); pool->thread_id = PulseGetThreadID();

View File

@@ -0,0 +1,17 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#include "VulkanComputePipeline.h"
PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info)
{
}
void VulkanBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline)
{
}
void VulkanDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline)
{
}

View File

@@ -16,7 +16,7 @@ typedef struct VulkanComputePipeline
} VulkanComputePipeline; } VulkanComputePipeline;
PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info); PulseComputePipeline VulkanCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info);
void VulkanBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline); void VulkanBindComputePipeline(PulseComputePipeline pipeline);
void VulkanDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline); void VulkanDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline);
#endif // PULSE_VULKAN_COMPUTE_PIPELINE_H_ #endif // PULSE_VULKAN_COMPUTE_PIPELINE_H_

View File

@@ -5,6 +5,7 @@
#include "Pulse.h" #include "Pulse.h"
#include "Vulkan.h" #include "Vulkan.h"
#include "VulkanComputePipeline.h" #include "VulkanComputePipeline.h"
#include "VulkanCommandList.h"
#include "VulkanDevice.h" #include "VulkanDevice.h"
#include "VulkanFence.h" #include "VulkanFence.h"
#include "VulkanInstance.h" #include "VulkanInstance.h"
@@ -175,9 +176,11 @@ PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devic
create_info.flags = 0; create_info.flags = 0;
create_info.pNext = PULSE_NULLPTR; create_info.pNext = PULSE_NULLPTR;
CHECK_VK_RETVAL(instance->vkCreateDevice(device->physical, &create_info, PULSE_NULLPTR, &device->device), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULLPTR); CHECK_VK_RETVAL(backend, instance->vkCreateDevice(device->physical, &create_info, PULSE_NULLPTR, &device->device), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULLPTR);
if(!VulkanLoadDevice(instance, device)) if(!VulkanLoadDevice(instance, device))
{ {
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(backend))
PulseLogInfoFmt(backend, "(Vulkan) Could not load device functions from %s", device->properties.deviceName);
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
return PULSE_NULLPTR; return PULSE_NULLPTR;
} }
@@ -217,11 +220,14 @@ PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devic
allocator_create_info.instance = instance->instance; allocator_create_info.instance = instance->instance;
allocator_create_info.pVulkanFunctions = &vma_vulkan_func; allocator_create_info.pVulkanFunctions = &vma_vulkan_func;
CHECK_VK_RETVAL(vmaCreateAllocator(&allocator_create_info, &device->allocator), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULLPTR); CHECK_VK_RETVAL(backend, vmaCreateAllocator(&allocator_create_info, &device->allocator), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULLPTR);
pulse_device->driver_data = device; pulse_device->driver_data = device;
pulse_device->backend = backend; pulse_device->backend = backend;
PULSE_LOAD_DRIVER_DEVICE(Vulkan); PULSE_LOAD_DRIVER_DEVICE(Vulkan);
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(backend))
PulseLogInfoFmt(backend, "(Vulkan) Created device from %s", device->properties.deviceName);
return pulse_device; return pulse_device;
} }
@@ -230,8 +236,35 @@ void VulkanDestroyDevice(PulseDevice device)
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE) if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE)
return; return;
for(uint32_t i = 0; i < vulkan_device->cmd_pools_size; i++)
vulkan_device->vkDestroyCommandPool(vulkan_device->device, vulkan_device->cmd_pools[i].pool, PULSE_NULLPTR);
vmaDestroyAllocator(vulkan_device->allocator); vmaDestroyAllocator(vulkan_device->allocator);
vulkan_device->vkDestroyDevice(vulkan_device->device, PULSE_NULLPTR); vulkan_device->vkDestroyDevice(vulkan_device->device, PULSE_NULLPTR);
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend))
PulseLogInfoFmt(device->backend, "(Vulkan) Destroyed device created from %s", vulkan_device->properties.deviceName);
free(vulkan_device->cmd_pools);
free(vulkan_device); free(vulkan_device);
free(device); free(device);
} }
VulkanCommandPool* VulkanRequestCmdPoolFromDevice(PulseDevice device, VulkanQueueType queue_type)
{
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULLPTR);
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE)
return PULSE_NULLPTR;
PulseThreadID thread_id = PulseGetThreadID();
for(uint32_t i = 0; i < vulkan_device->cmd_pools_size; i++)
{
if(thread_id == vulkan_device->cmd_pools[i].thread_id && queue_type == vulkan_device->cmd_pools[i].queue_type)
return &vulkan_device->cmd_pools[i];
}
vulkan_device->cmd_pools_size++;
vulkan_device->cmd_pools = (VulkanCommandPool*)realloc(vulkan_device->cmd_pools, vulkan_device->cmd_pools_size * sizeof(VulkanCommandPool));
if(!VulkanInitCommandPool(device, &vulkan_device->cmd_pools[vulkan_device->cmd_pools_size - 1], queue_type))
return PULSE_NULLPTR;
return &vulkan_device->cmd_pools[vulkan_device->cmd_pools_size - 1];
}

View File

@@ -24,6 +24,7 @@ struct VulkanQueue;
typedef struct VulkanDevice typedef struct VulkanDevice
{ {
VulkanCommandPool* cmd_pools; VulkanCommandPool* cmd_pools;
uint32_t cmd_pools_size;
struct VulkanQueue* queues[VULKAN_QUEUE_END_ENUM]; struct VulkanQueue* queues[VULKAN_QUEUE_END_ENUM];
@@ -43,6 +44,7 @@ typedef struct VulkanDevice
PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count); PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count);
void VulkanDestroyDevice(PulseDevice device); void VulkanDestroyDevice(PulseDevice device);
VulkanCommandPool* VulkanRequestCmdPoolFromDevice(PulseDevice device, VulkanQueueType queue_type);
#endif // PULSE_VULKAN_DEVICE_H_ #endif // PULSE_VULKAN_DEVICE_H_

View File

@@ -10,12 +10,14 @@
PulseFence VulkanCreateFence(PulseDevice device) PulseFence VulkanCreateFence(PulseDevice device)
{ {
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE);
VkFenceCreateInfo fence_info = {}; VkFenceCreateInfo fence_info = {};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VkFence vulkan_fence; VkFence vulkan_fence;
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
CHECK_VK_RETVAL(vulkan_device->vkCreateFence(vulkan_device->device, &fence_info, PULSE_NULLPTR, &vulkan_fence), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE); CHECK_VK_RETVAL(device->backend, vulkan_device->vkCreateFence(vulkan_device->device, &fence_info, PULSE_NULLPTR, &vulkan_fence), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE);
PulseFenceHandler* fence = (PulseFenceHandler*)malloc(sizeof(PulseFenceHandler)); PulseFenceHandler* fence = (PulseFenceHandler*)malloc(sizeof(PulseFenceHandler));
PULSE_CHECK_ALLOCATION_RETVAL(fence, PULSE_NULL_HANDLE); PULSE_CHECK_ALLOCATION_RETVAL(fence, PULSE_NULL_HANDLE);
@@ -25,6 +27,15 @@ PulseFence VulkanCreateFence(PulseDevice device)
void VulkanDestroyFence(PulseDevice device, PulseFence fence) void VulkanDestroyFence(PulseDevice device, PulseFence fence)
{ {
PULSE_CHECK_HANDLE(device);
if(fence == PULSE_NULL_HANDLE)
{
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogWarning(device->backend, "fence is NULL, this may be a bug in your application");
return;
}
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE) if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE)
return; return;
@@ -37,6 +48,9 @@ void VulkanDestroyFence(PulseDevice device, PulseFence fence)
bool VulkanIsFenceReady(PulseDevice device, PulseFence fence) bool VulkanIsFenceReady(PulseDevice device, PulseFence fence)
{ {
PULSE_CHECK_HANDLE_RETVAL(device, false);
PULSE_CHECK_HANDLE_RETVAL(fence, false);
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE) if(vulkan_device == PULSE_NULLPTR || vulkan_device->device == VK_NULL_HANDLE)
return false; return false;
@@ -66,7 +80,7 @@ bool VulkanWaitForFences(PulseDevice device, const PulseFence* fences, uint32_t
VkFence* vulkan_fences = (VkFence*)calloc(fences_count, sizeof(VkFence)); VkFence* vulkan_fences = (VkFence*)calloc(fences_count, sizeof(VkFence));
PULSE_CHECK_ALLOCATION_RETVAL(vulkan_fences, false); PULSE_CHECK_ALLOCATION_RETVAL(vulkan_fences, false);
for(uint32_t i = 0; i < fences_count; i++) for(uint32_t i = 0; i < fences_count; i++)
vulkan_fences[i] = VULKAN_RETRIEVE_DRIVER_DATA_AS(((PulseFence)fences + i), VkFence); vulkan_fences[i] = VULKAN_RETRIEVE_DRIVER_DATA_AS(((PulseFence)fences[i]), VkFence);
VkResult result = vulkan_device->vkWaitForFences(vulkan_device->device, fences_count, vulkan_fences, wait_for_all, UINT64_MAX); VkResult result = vulkan_device->vkWaitForFences(vulkan_device->device, fences_count, vulkan_fences, wait_for_all, UINT64_MAX);
free(vulkan_fences); free(vulkan_fences);
switch(result) switch(result)

View File

@@ -36,7 +36,7 @@ static VkInstance VulkanCreateInstance(const char** extensions_enabled, uint32_t
create_info.flags = 0; create_info.flags = 0;
#endif #endif
CHECK_VK_RETVAL(VulkanGetGlobal()->vkCreateInstance(&create_info, PULSE_NULLPTR, &instance), PULSE_ERROR_INITIALIZATION_FAILED, VK_NULL_HANDLE); CHECK_VK_RETVAL(PULSE_NULL_HANDLE, VulkanGetGlobal()->vkCreateInstance(&create_info, PULSE_NULLPTR, &instance), PULSE_ERROR_INITIALIZATION_FAILED, VK_NULL_HANDLE);
return instance; return instance;
} }

View File

@@ -2,7 +2,8 @@
// This file is part of "Pulse" // This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE // For conditions of distribution and use, see copyright notice in LICENSE
#include <string.h> #include <stdio.h>
#include <stdarg.h>
#include <Pulse.h> #include <Pulse.h>
#include "PulseInternal.h" #include "PulseInternal.h"
@@ -25,36 +26,41 @@ static const PulseCheckBackendSupportPFN backends_supports[] = {
PULSE_NULLPTR PULSE_NULLPTR
}; };
struct static PulseErrorType last_error = PULSE_ERROR_NONE;
{
char file[1024];
char function[1024];
PulseErrorType type;
int line;
} last_error = { .file = { 0 }, .function = { 0 }, .type = PULSE_ERROR_NONE, .line = -1 };
void PulseSetInternalErrorBackend(PulseErrorType error, const char* file, const char* function, int line) void PulseSetInternalError(PulseErrorType error)
{ {
strcpy(last_error.file, file); last_error = error;
strcpy(last_error.function, function);
last_error.type = error;
last_error.line = line;
} }
void PulseLogErrorBackend(PulseBackend backend, PulseErrorType error, const char* file, const char* function, int line) #define LOG_MESSAGE_MAX_LENGTH 4096
void PulseLogBackend(PulseBackend backend, PulseDebugMessageSeverity type, const char* message, const char* file, const char* function, int line, ...)
{ {
(void)file; // May be used later
if(backend == PULSE_NULL_HANDLE) if(backend == PULSE_NULL_HANDLE)
return; return;
if(!backend->PFN_UserDebugCallback) if(!backend->PFN_UserDebugCallback)
return; return;
}
void PulseLogWarningBackend(PulseBackend backend, PulseWarningType warning, const char* file, const char* function, int line) va_list argptr;
{ va_start(argptr, line);
}
void PulseLogInfoBackend(PulseBackend backend, const char* message, const char* file, const char* function, int line) char complete_message[LOG_MESSAGE_MAX_LENGTH] = { 0 };
int shift = 0;
if(type != PULSE_DEBUG_MESSAGE_SEVERITY_INFO)
{ {
shift = snprintf(complete_message, LOG_MESSAGE_MAX_LENGTH, "[%s:%d] ", function, line);
if(backend->debug_level == PULSE_PARANOID_DEBUG && type == PULSE_DEBUG_MESSAGE_SEVERITY_WARNING)
type = PULSE_DEBUG_MESSAGE_SEVERITY_ERROR;
if(shift == -1)
shift = 0;
}
vsnprintf(complete_message + shift, LOG_MESSAGE_MAX_LENGTH - shift, message, argptr);
backend->PFN_UserDebugCallback(type, complete_message);
va_end(argptr);
} }
static PulseBackendFlags PulseSelectBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used) static PulseBackendFlags PulseSelectBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used)
@@ -74,10 +80,6 @@ static PulseBackendFlags PulseSelectBackend(PulseBackendFlags backend_candidates
return PULSE_BACKEND_INVALID; return PULSE_BACKEND_INVALID;
} }
static const char* PulseVerbaliseErrorType(PulseErrorType error)
{
}
static PulseBackend PulseGetBackendFromFlag(PulseBackendBits flag) static PulseBackend PulseGetBackendFromFlag(PulseBackendBits flag)
{ {
switch(flag) switch(flag)
@@ -86,7 +88,7 @@ static PulseBackend PulseGetBackendFromFlag(PulseBackendBits flag)
case PULSE_BACKEND_VULKAN: return &VulkanDriver; case PULSE_BACKEND_VULKAN: return &VulkanDriver;
#endif #endif
#ifdef PULSE_ENABLE_D3D11_BACKEND #ifdef PULSE_ENABLE_D3D11_BACKEND
case PULSE_BACKEND_VULKAN: return &D3D11Driver; case PULSE_BACKEND_D3D11: return &D3D11Driver;
#endif #endif
default: break; default: break;
@@ -106,6 +108,7 @@ PULSE_API PulseBackend PulseLoadBackend(PulseBackendFlags backend_candidates, Pu
if(!backend->PFN_LoadBackend(debug_level)) if(!backend->PFN_LoadBackend(debug_level))
return PULSE_NULL_HANDLE; return PULSE_NULL_HANDLE;
backend->PFN_UserDebugCallback = PULSE_NULLPTR; backend->PFN_UserDebugCallback = PULSE_NULLPTR;
backend->debug_level = debug_level;
return (PulseBackend)backend; return (PulseBackend)backend;
} }
@@ -115,6 +118,12 @@ PULSE_API void PulseUnloadBackend(PulseBackend backend)
backend->PFN_UnloadBackend(backend); backend->PFN_UnloadBackend(backend);
} }
PULSE_API PulseBackendFlags PulseGetBackendType(PulseBackend backend)
{
PULSE_CHECK_HANDLE_RETVAL(backend, PULSE_BACKEND_INVALID);
return backend->backend;
}
PULSE_API bool PulseSupportsBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used) PULSE_API bool PulseSupportsBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used)
{ {
if((backend_candidates & PULSE_BACKEND_INVALID) != 0) if((backend_candidates & PULSE_BACKEND_INVALID) != 0)
@@ -133,3 +142,27 @@ PULSE_API void PulseSetDebugCallback(PulseBackend backend, PulseDebugCallbackPFN
PULSE_CHECK_HANDLE(backend); PULSE_CHECK_HANDLE(backend);
backend->PFN_UserDebugCallback = callback; backend->PFN_UserDebugCallback = callback;
} }
PULSE_API PulseErrorType PulseGetLastErrorType()
{
PulseErrorType error = last_error;
last_error = PULSE_ERROR_NONE;
return error;
}
PULSE_API const char* PulseVerbaliseErrorType(PulseErrorType error)
{
switch(error)
{
case PULSE_ERROR_NONE: return "no error";
case PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH: return "no backend candidates support the required shader formats";
case PULSE_ERROR_INITIALIZATION_FAILED: return "initialization of an object could not be completed for implementation-specific reasons";
case PULSE_ERROR_CPU_ALLOCATION_FAILED: return "an internal CPU allocation failed";
case PULSE_ERROR_DEVICE_ALLOCATION_FAILED: return "a device allocation failed";
case PULSE_ERROR_DEVICE_LOST: return "device has been lost";
case PULSE_ERROR_INVALID_INTERNAL_POINTER: return "invalid internal pointer";
default: return "invalid error type";
};
return PULSE_NULLPTR; // To avoid warnings, should be unreachable
}

71
Sources/PulseCommandList.c git.filemode.normal_file
View File

@@ -0,0 +1,71 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#include "PulseDefs.h"
#include "PulseInternal.h"
PULSE_API PulseCommandList PulseRequestCommandList(PulseDevice device, PulseCommandListUsage usage)
{
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE);
return device->PFN_RequestCommandList(device, usage);
}
PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence)
{
PULSE_CHECK_HANDLE_RETVAL(device, false);
PULSE_CHECK_HANDLE_RETVAL(cmd, false);
if(cmd->state != PULSE_COMMAND_LIST_STATE_READY)
{
switch(cmd->state)
{
case PULSE_COMMAND_LIST_STATE_INVALID:
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogError(device->backend, "command list is in invalid state");
return false;
case PULSE_COMMAND_LIST_STATE_EMPTY:
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogWarning(device->backend, "command list is empty");
return false;
case PULSE_COMMAND_LIST_STATE_RECORDING:
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogError(device->backend, "command list is in recording state");
return false;
case PULSE_COMMAND_LIST_STATE_SENT:
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogWarning(device->backend, "command list has already been submitted");
return false;
default: break;
}
}
return device->PFN_SubmitCommandList(device, cmd, fence);
}
PULSE_API void PulseReleaseCommandList(PulseDevice device, PulseCommandList cmd)
{
PULSE_CHECK_HANDLE(device);
if(cmd == PULSE_NULL_HANDLE)
{
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogWarning(device->backend, "command list is NULL, this may be a bug in your application");
return;
}
switch(cmd->state)
{
case PULSE_COMMAND_LIST_STATE_RECORDING:
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
PulseLogWarning(device->backend, "command list is in recording state");
break;
default: break;
}
return device->PFN_ReleaseCommandList(device, cmd);
}

View File

@@ -10,9 +10,8 @@ PULSE_API PulseComputePipeline PulseCreateComputePipeline(PulseDevice device, co
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE); PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE);
} }
PULSE_API void PulseBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline) PULSE_API void PulseBindComputePipeline(PulseComputePipeline pipeline)
{ {
PULSE_CHECK_HANDLE(pass);
PULSE_CHECK_HANDLE(pipeline); PULSE_CHECK_HANDLE(pipeline);
} }

View File

@@ -3,30 +3,3 @@
// For conditions of distribution and use, see copyright notice in LICENSE // For conditions of distribution and use, see copyright notice in LICENSE
#include <PulseProfile.h> #include <PulseProfile.h>
#include "PulseDebug.h"
const char* PulseVerbaliseErrorType(PulseErrorType error)
{
switch(error)
{
case PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH: return "no backend candidates support the required shader formats";
case PULSE_ERROR_INITIALIZATION_FAILED: return "initialization of an object could not be completed for implementation-specific reasons";
case PULSE_ERROR_CPU_ALLOCATION_FAILED: return "a CPU allocation failed";
case PULSE_ERROR_DEVICE_ALLOCATION_FAILED: return "a device allocation failed";
case PULSE_ERROR_DEVICE_LOST: return "device has been lost";
case PULSE_ERROR_INVALID_INTERNAL_POINTER: return "invalid internal pointer";
case PULSE_ERROR_INVALID_HANDLE: return "invalid handle";
default: return "invalid error type";
};
return PULSE_NULLPTR; // To avoid warnings, should be unreachable
}
const char* PulseVerbaliseWarningType(PulseWarningType warning)
{
switch(warning)
{
default: return "invalid warning type";
};
return PULSE_NULLPTR; // To avoid warnings, should be unreachable
}

View File

@@ -1,13 +0,0 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#ifndef PULSE_DEBUG_H_
#define PULSE_DEBUG_H_
#include "PulseEnums.h"
const char* PulseVerbaliseErrorType(PulseErrorType error);
const char* PulseVerbaliseWarningType(PulseWarningType warning);
#endif

View File

@@ -40,6 +40,13 @@
} \ } \
} while(0); \ } while(0); \
#define PULSE_UNUSED(x) ((void)x)
#define PULSE_CHECK_BACKEND_DEBUG_LEVEL(backend, level) (backend != PULSE_NULL_HANDLE && ((PulseBackend)backend)->debug_level >= level)
#define PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(backend) PULSE_CHECK_BACKEND_DEBUG_LEVEL(backend, PULSE_LOW_DEBUG)
#define PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(backend) PULSE_CHECK_BACKEND_DEBUG_LEVEL(backend, PULSE_HIGH_DEBUG)
#define PULSE_CHECK_PTR(handle) PULSE_CHECK_PTR_RETVAL(handle, ) #define PULSE_CHECK_PTR(handle) PULSE_CHECK_PTR_RETVAL(handle, )
#define PULSE_LOAD_DRIVER_DEVICE_FUNCTION(fn, _namespace) pulse_device->PFN_##fn = _namespace##fn; #define PULSE_LOAD_DRIVER_DEVICE_FUNCTION(fn, _namespace) pulse_device->PFN_##fn = _namespace##fn;
@@ -52,5 +59,8 @@
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyFence, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyFence, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(IsFenceReady, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(IsFenceReady, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(WaitForFences, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(WaitForFences, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(RequestCommandList, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(SubmitCommandList, _namespace) \
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(ReleaseCommandList, _namespace) \
#endif // PULSE_DEFS_H_ #endif // PULSE_DEFS_H_

View File

@@ -14,22 +14,4 @@ typedef enum PulseCommandListState
PULSE_COMMAND_LIST_STATE_SENT PULSE_COMMAND_LIST_STATE_SENT
} PulseCommandListState; } PulseCommandListState;
typedef enum PulseErrorType
{
PULSE_ERROR_NONE,
PULSE_ERROR_DEVICE_LOST,
PULSE_ERROR_INVALID_HANDLE,
PULSE_ERROR_INVALID_INTERNAL_POINTER,
PULSE_ERROR_INITIALIZATION_FAILED,
PULSE_ERROR_CPU_ALLOCATION_FAILED,
PULSE_ERROR_DEVICE_ALLOCATION_FAILED,
PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH,
} PulseErrorType;
typedef enum PulseWarningType
{
PULSE_WARNING_NONE,
} PulseWarningType;
#endif #endif

32
Sources/PulseFence.c git.filemode.normal_file
View File

@@ -0,0 +1,32 @@
// Copyright (C) 2024 kanel
// This file is part of "Pulse"
// For conditions of distribution and use, see copyright notice in LICENSE
#include "PulseDefs.h"
#include "PulseInternal.h"
PULSE_API PulseFence PulseCreateFence(PulseDevice device)
{
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE);
return device->PFN_CreateFence(device);
}
PULSE_API void PulseDestroyFence(PulseDevice device, PulseFence fence)
{
PULSE_CHECK_HANDLE(device);
return device->PFN_DestroyFence(device, fence);
}
PULSE_API bool PulseIsFenceReady(PulseDevice device, PulseFence fence)
{
PULSE_CHECK_HANDLE_RETVAL(device, false);
PULSE_CHECK_HANDLE_RETVAL(fence, false);
return device->PFN_IsFenceReady(device, fence);
}
PULSE_API bool PulseWaitForFences(PulseDevice device, const PulseFence* fences, uint32_t fences_count, bool wait_for_all)
{
PULSE_CHECK_HANDLE_RETVAL(device, false);
PULSE_CHECK_PTR_RETVAL(fences, false);
return device->PFN_WaitForFences(device, fences, fences_count, wait_for_all);
}

View File

@@ -25,6 +25,7 @@ typedef struct PulseBackendHandler
PulseShaderFormatsFlags supported_shader_formats; PulseShaderFormatsFlags supported_shader_formats;
void* driver_data; void* driver_data;
PulseDebugCallbackPFN PFN_UserDebugCallback; PulseDebugCallbackPFN PFN_UserDebugCallback;
PulseDebugLevel debug_level;
} PulseBackendHandler; } PulseBackendHandler;
typedef struct PulseBufferHandler typedef struct PulseBufferHandler
@@ -36,15 +37,16 @@ typedef struct PulseCommandListHandler
{ {
PulseDevice device; PulseDevice device;
void* driver_data; void* driver_data;
PulseThreadID thread_id;
PulseComputePipeline* compute_pipelines_bound;
uint32_t compute_pipelines_bound_capacity;
uint32_t compute_pipelines_bound_size;
PulseCommandListState state; PulseCommandListState state;
PulseCommandListUsage usage;
bool is_compute_pipeline_bound; bool is_compute_pipeline_bound;
bool is_available;
} PulseCommandListHandler; } PulseCommandListHandler;
typedef struct PulseComputePassHandler
{
void* driver_data;
} PulseComputePassHandler;
typedef struct PulseComputePipelineHandler typedef struct PulseComputePipelineHandler
{ {
void* driver_data; void* driver_data;
@@ -61,6 +63,9 @@ typedef struct PulseDeviceHandler
PulseDestroyFencePFN PFN_DestroyFence; PulseDestroyFencePFN PFN_DestroyFence;
PulseIsFenceReadyPFN PFN_IsFenceReady; PulseIsFenceReadyPFN PFN_IsFenceReady;
PulseWaitForFencesPFN PFN_WaitForFences; PulseWaitForFencesPFN PFN_WaitForFences;
PulseRequestCommandListPFN PFN_RequestCommandList;
PulseSubmitCommandListPFN PFN_SubmitCommandList;
PulseReleaseCommandListPFN PFN_ReleaseCommandList;
// Attributes // Attributes
void* driver_data; void* driver_data;
@@ -72,11 +77,6 @@ typedef struct PulseFenceHandler
void* driver_data; void* driver_data;
} PulseFenceHandler; } PulseFenceHandler;
typedef struct PulseGeneralPassHandler
{
void* driver_data;
} PulseGeneralPassHandler;
typedef struct PulseImageHandler typedef struct PulseImageHandler
{ {
void* driver_data; void* driver_data;
@@ -84,13 +84,17 @@ typedef struct PulseImageHandler
PulseThreadID PulseGetThreadID(); PulseThreadID PulseGetThreadID();
void PulseLogErrorBackend(PulseBackend backend, PulseErrorType error, const char* file, const char* function, int line); void PulseSetInternalError(PulseErrorType error);
void PulseLogWarningBackend(PulseBackend backend, PulseWarningType warning, const char* file, const char* function, int line);
void PulseLogInfoBackend(PulseBackend backend, const char* message, const char* file, const char* function, int line);
#define PulseLogError(backend, type) PulseSetInternalErrorBackend(backend, type, __FILE__, __FUNCTION__, __LINE__) void PulseLogBackend(PulseBackend backend, PulseDebugMessageSeverity type, const char* message, const char* file, const char* function, int line, ...);
#define PulseLogWarning(backend, type) PulseSetInternalErrorBackend(backend, type, __FILE__, __FUNCTION__, __LINE__)
#define PulseLogInfo(backend, msg) PulseSetInternalErrorBackend(backend, msg, __FILE__, __FUNCTION__, __LINE__) #define PulseLogError(backend, msg) PulseLogBackend(backend, PULSE_DEBUG_MESSAGE_SEVERITY_ERROR, msg, __FILE__, __FUNCTION__, __LINE__)
#define PulseLogWarning(backend, msg) PulseLogBackend(backend, PULSE_DEBUG_MESSAGE_SEVERITY_WARNING, msg, __FILE__, __FUNCTION__, __LINE__)
#define PulseLogInfo(backend, msg) PulseLogBackend(backend, PULSE_DEBUG_MESSAGE_SEVERITY_INFO, msg, __FILE__, __FUNCTION__, __LINE__)
#define PulseLogErrorFmt(backend, msg, ...) PulseLogBackend(backend, PULSE_DEBUG_MESSAGE_SEVERITY_ERROR, msg, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define PulseLogWarningFmt(backend, msg, ...) PulseLogBackend(backend, PULSE_DEBUG_MESSAGE_SEVERITY_WARNING, msg, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define PulseLogInfoFmt(backend, msg, ...) PulseLogBackend(backend, PULSE_DEBUG_MESSAGE_SEVERITY_INFO, msg, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#ifdef PULSE_ENABLE_VULKAN_BACKEND #ifdef PULSE_ENABLE_VULKAN_BACKEND
extern PulseBackendHandler VulkanDriver; extern PulseBackendHandler VulkanDriver;

View File

@@ -15,11 +15,14 @@ typedef PulseDevice (*PulseCreateDevicePFN)(PulseBackend, PulseDevice*, uint32_t
typedef void (*PulseDestroyDevicePFN)(PulseDevice); typedef void (*PulseDestroyDevicePFN)(PulseDevice);
typedef PulseComputePipeline (*PulseCreateComputePipelinePFN)(PulseDevice, const PulseComputePipelineCreateInfo*); typedef PulseComputePipeline (*PulseCreateComputePipelinePFN)(PulseDevice, const PulseComputePipelineCreateInfo*);
typedef void (*PulseBindComputePipelinePFN)(PulseComputePass, PulseComputePipeline); typedef void (*PulseBindComputePipelinePFN)(PulseComputePipeline);
typedef void (*PulseDestroyComputePipelinePFN)(PulseDevice, PulseComputePipeline); typedef void (*PulseDestroyComputePipelinePFN)(PulseDevice, PulseComputePipeline);
typedef PulseFence (*PulseCreateFencePFN)(PulseDevice); typedef PulseFence (*PulseCreateFencePFN)(PulseDevice);
typedef void (*PulseDestroyFencePFN)(PulseDevice, PulseFence); typedef void (*PulseDestroyFencePFN)(PulseDevice, PulseFence);
typedef bool (*PulseIsFenceReadyPFN)(PulseDevice, PulseFence); typedef bool (*PulseIsFenceReadyPFN)(PulseDevice, PulseFence);
typedef bool (*PulseWaitForFencesPFN)(PulseDevice, const PulseFence*, uint32_t, bool); typedef bool (*PulseWaitForFencesPFN)(PulseDevice, const PulseFence*, uint32_t, bool);
typedef PulseCommandList (*PulseRequestCommandListPFN)(PulseDevice, PulseCommandListUsage);
typedef bool (*PulseSubmitCommandListPFN)(PulseDevice, PulseCommandList, PulseFence);
typedef void (*PulseReleaseCommandListPFN)(PulseDevice, PulseCommandList);
#endif // PULSE_PFNS_H_ #endif // PULSE_PFNS_H_

42
Tests/Vulkan/Backend.c git.filemode.normal_file
View File

@@ -0,0 +1,42 @@
#include "Common.h"
#include <unity/unity.h>
#include <Pulse.h>
void DumbDebugCallBack(PulseDebugMessageSeverity severity, const char* message)
{
(void)severity;
(void)message;
}
void TestBackendSetup()
{
PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_VULKAN, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_HIGH_DEBUG);
TEST_ASSERT_NOT_EQUAL_MESSAGE(backend, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
PulseSetDebugCallback(backend, DumbDebugCallBack);
PulseUnloadBackend(backend);
}
void TestBackendAnySetup()
{
PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_ANY, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_HIGH_DEBUG);
TEST_ASSERT_NOT_EQUAL_MESSAGE(backend, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
TEST_ASSERT_EQUAL(PulseGetBackendType(backend), PULSE_BACKEND_VULKAN);
PulseSetDebugCallback(backend, DumbDebugCallBack);
PulseUnloadBackend(backend);
}
void TestWrongBackendSetup()
{
PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_VULKAN, PULSE_SHADER_FORMAT_DXBC_BIT, PULSE_HIGH_DEBUG);
TEST_ASSERT_EQUAL(backend, PULSE_NULL_HANDLE);
PulseSetDebugCallback(backend, DumbDebugCallBack);
PulseUnloadBackend(backend);
}
void TestBackend()
{
RUN_TEST(TestBackendSetup);
RUN_TEST(TestBackendAnySetup);
RUN_TEST(TestWrongBackendSetup);
}

28
Tests/Vulkan/Common.c git.filemode.normal_file
View File

@@ -0,0 +1,28 @@
#include "Common.h"
#include <unity/unity.h>
void DebugCallBack(PulseDebugMessageSeverity severity, const char* message)
{
if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_ERROR)
TEST_FAIL_MESSAGE(message);
}
#define LOG_MESSAGE_MAX_LENGTH 4096
void SetupPulse(PulseBackend* backend)
{
*backend = PulseLoadBackend(PULSE_BACKEND_VULKAN, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_PARANOID_DEBUG);
if(*backend == PULSE_NULL_HANDLE)
{
char complete_message[LOG_MESSAGE_MAX_LENGTH] = { 0 };
snprintf(complete_message, LOG_MESSAGE_MAX_LENGTH, "Fatal Error: could not load Pulse using Vulkan due to %s", PulseVerbaliseErrorType(PulseGetLastErrorType()));
TEST_FAIL_MESSAGE(complete_message);
TEST_ABORT();
}
PulseSetDebugCallback(*backend, DebugCallBack);
}
void CleanupPulse(PulseBackend backend)
{
PulseUnloadBackend(backend);
}

26
Tests/Vulkan/Common.h git.filemode.normal_file
View File

@@ -0,0 +1,26 @@
#ifndef TEST_COMMON_H_
#define TEST_COMMON_H_
#define RUN_TEST_AT_LINE(fn, line) \
{ \
Unity.TestFile = __FILE__; \
Unity.CurrentTestName = #fn; \
Unity.CurrentTestLineNumber = line; \
Unity.NumberOfTests++; \
UNITY_CLR_DETAILS(); \
UNITY_EXEC_TIME_START(); \
if(TEST_PROTECT()) \
fn(); \
UNITY_EXEC_TIME_STOP(); \
UnityConcludeTest(); \
}
#define RUN_TEST(fn) RUN_TEST_AT_LINE(fn, __LINE__);
#include <Pulse.h>
void DebugCallBack(PulseDebugMessageSeverity severity, const char* message);
void SetupPulse(PulseBackend* backend);
void CleanupPulse(PulseBackend backend);
#endif

75
Tests/Vulkan/Device.c git.filemode.normal_file
View File

@@ -0,0 +1,75 @@
#include "Common.h"
#include <unity/unity.h>
#include <Pulse.h>
void TestDeviceSetup()
{
PulseBackend backend;
SetupPulse(&backend);
PulseDevice device = PulseCreateDevice(backend, NULL, 0);
TEST_ASSERT_NOT_EQUAL_MESSAGE(device, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
PulseDestroyDevice(device);
CleanupPulse(backend);
}
void TestForbiddenDeviceSetup()
{
PulseBackend backend;
SetupPulse(&backend);
PulseDevice device1 = PulseCreateDevice(backend, NULL, 0);
TEST_ASSERT_NOT_EQUAL_MESSAGE(device1, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
PulseDevice device2 = PulseCreateDevice(backend, &device1, 1);
TEST_ASSERT_NOT_EQUAL(device2, device1);
PulseDestroyDevice(device2);
PulseDestroyDevice(device1);
CleanupPulse(backend);
}
void TestInvalidBackendDeviceSetup()
{
PulseDevice device= PulseCreateDevice(PULSE_NULL_HANDLE, NULL, 0);
TEST_ASSERT_EQUAL(device, PULSE_NULL_HANDLE);
PulseDestroyDevice(device);
}
void TestBackendInUse()
{
PulseBackend backend;
SetupPulse(&backend);
PulseDevice device = PulseCreateDevice(backend, NULL, 0);
TEST_ASSERT_NOT_EQUAL_MESSAGE(device, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
TEST_ASSERT_EQUAL(PulseGetBackendInUseByDevice(device), PULSE_BACKEND_VULKAN);
PulseDestroyDevice(device);
CleanupPulse(backend);
}
void TestShaderFormatSupport()
{
PulseBackend backend;
SetupPulse(&backend);
PulseDevice device = PulseCreateDevice(backend, NULL, 0);
TEST_ASSERT_NOT_EQUAL_MESSAGE(device, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
TEST_ASSERT_TRUE(PulseDeviceSupportsShaderFormats(device, PULSE_SHADER_FORMAT_SPIRV_BIT));
PulseDestroyDevice(device);
CleanupPulse(backend);
}
void TestDevice()
{
RUN_TEST(TestDeviceSetup);
RUN_TEST(TestForbiddenDeviceSetup);
RUN_TEST(TestInvalidBackendDeviceSetup);
RUN_TEST(TestBackendInUse);
RUN_TEST(TestShaderFormatSupport);
}

View File

@@ -1,11 +0,0 @@
#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);
}

View File

@@ -3,39 +3,13 @@
#include <stdbool.h> #include <stdbool.h>
PulseBackend backend; extern void TestBackend();
extern void TestDevice();
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) int main(void)
{ {
SetupPulse(); UNITY_BEGIN();
int result = RunUnitTests(); TestBackend();
UnloadPulse(); TestDevice();
return result; return UNITY_END();
} }