From f189928c82c0f05b2ed83458e6154e5072650e5f Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Wed, 9 Oct 2024 18:56:41 +0200 Subject: [PATCH] adding backend management responsability to the user --- Includes/Pulse.h | 7 +- README.md | 3 + Sources/Backends/Vulkan/Vulkan.c | 57 ++++++----- Sources/Backends/Vulkan/Vulkan.h | 8 +- Sources/Backends/Vulkan/VulkanDevice.c | 93 ++++++++++++++++++ Sources/Backends/Vulkan/VulkanDevice.h | 2 +- Sources/Backends/Vulkan/VulkanInstance.c | 21 +++- Sources/Backends/Vulkan/VulkanLoader.c | 9 ++ Sources/PulseBackend.c | 118 +++++++++++++++++++++++ Sources/PulseDevice.c | 101 +------------------ Sources/PulseInternal.h | 34 +++++-- Tests/LoadingPulse/main.c | 7 +- 12 files changed, 321 insertions(+), 139 deletions(-) create mode 100644 README.md create mode 100644 Sources/PulseBackend.c diff --git a/Includes/Pulse.h b/Includes/Pulse.h index 5ba512b..221f9e5 100644 --- a/Includes/Pulse.h +++ b/Includes/Pulse.h @@ -22,6 +22,7 @@ extern "C" { typedef uint64_t PulseDeviceSize; typedef uint32_t PulseFlags; +PULSE_DEFINE_NULLABLE_HANDLE(PulseBackend); PULSE_DEFINE_NULLABLE_HANDLE(PulseBuffer); PULSE_DEFINE_NULLABLE_HANDLE(PulseCommandList); PULSE_DEFINE_NULLABLE_HANDLE(PulseComputePass); @@ -249,11 +250,13 @@ typedef struct PulseImageRegion } PulseImageRegion; // Functions -PULSE_API PulseDevice PulseCreateDevice(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 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 PulseDeviceSupportsSahderFormats(PulseDevice device, PulseShaderFormatsFlags shader_formats_used); +PULSE_API bool PulseDeviceSupportsShaderFormats(PulseDevice device, PulseShaderFormatsFlags shader_formats_used); PULSE_API PulseErrorType PulseGetLastErrorType(); // /!\ Warning /!\ Call to this function resets the internal last error variable PULSE_API const char* PulseVerbaliseErrorType(PulseErrorType error); diff --git a/README.md b/README.md new file mode 100644 index 0000000..a976173 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# PulseGPU + +Pulse is a low level GPGPU library designed for higly intensive general GPU computations with high control over the hardware. It is built on top of Vulkan, D3D11 and a Metal support is in discussion. diff --git a/Sources/Backends/Vulkan/Vulkan.c b/Sources/Backends/Vulkan/Vulkan.c index ee5cf09..d22d2e2 100644 --- a/Sources/Backends/Vulkan/Vulkan.c +++ b/Sources/Backends/Vulkan/Vulkan.c @@ -9,6 +9,7 @@ #include "Vulkan.h" #include "VulkanLoader.h" +#include "VulkanInstance.h" VulkanGlobal* VulkanGetGlobal() { @@ -16,37 +17,49 @@ VulkanGlobal* VulkanGetGlobal() return &vulkan; } -bool VulkanLoadBackend() +PulseBackendFlags VulkanCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used) +{ + if(candidates != PULSE_BACKEND_ANY && (candidates & PULSE_BACKEND_VULKAN) == 0) + return PULSE_BACKEND_INVALID; + if((shader_formats_used & PULSE_SHADER_FORMAT_SPIRV_BIT) == 0) + return PULSE_BACKEND_INVALID; + + if(!VulkanInitLoader()) + return PULSE_BACKEND_INVALID; + VulkanInstance instance; + if(!VulkanInitInstance(&instance, PULSE_NO_DEBUG)) // Instance creation will fail if Vulkan is not supported + { + PulseGetLastErrorType(); // Clearing out the errors set by the failed instance creation + return PULSE_BACKEND_INVALID; + } + VulkanDestroyInstance(&instance); + VulkanLoaderShutdown(); + return PULSE_BACKEND_VULKAN; +} + +bool VulkanLoadBackend(PulseDebugLevel debug_level) { if(!VulkanInitLoader()) return false; + VulkanDriverData* driver_data = (VulkanDriverData*)calloc(1, sizeof(VulkanDriverData)); + PULSE_CHECK_ALLOCATION_RETVAL(driver_data, false); + if(!VulkanInitInstance(&driver_data->instance, debug_level)) + return false; + VulkanDriver.driver_data = driver_data; return true; } -PulseDevice VulkanCreatePulseDevice(PulseDebugLevel debug_level) +void VulkanUnloadBackend(PulseBackend backend) { - VulkanPulseDevice* vulkan_device = (VulkanPulseDevice*)calloc(1, sizeof(VulkanPulseDevice)); - PULSE_CHECK_ALLOCATION_RETVAL(vulkan_device, PULSE_NULL_HANDLE); - - if(!VulkanInitInstance(&vulkan_device->instance, debug_level)) - return PULSE_NULL_HANDLE; - - PulseDeviceHandler* pulse_device = (PulseDeviceHandler*)calloc(1, sizeof(PulseDeviceHandler)); - PULSE_CHECK_ALLOCATION_RETVAL(pulse_device, PULSE_NULL_HANDLE); - pulse_device->debug_level = debug_level; - pulse_device->driver_data = vulkan_device; - return pulse_device; + VulkanDestroyInstance(&VULKAN_RETRIEVE_DRIVER_DATA(backend)->instance); + VulkanLoaderShutdown(); } -void VulkanDestroyPulseDevice(PulseDevice device) -{ - -} - -PulseBackendLoader VulkanDriver = { +PulseBackendHandler VulkanDriver = { .PFN_LoadBackend = VulkanLoadBackend, - .PFN_CreateDevice = VulkanCreatePulseDevice, - .PFN_DestroyDevice = VulkanDestroyPulseDevice, + .PFN_UnloadBackend = VulkanUnloadBackend, + .PFN_CreateDevice = VulkanCreateDevice, .backend = PULSE_BACKEND_VULKAN, - .supported_shader_formats = PULSE_SHADER_FORMAT_SPIRV_BIT + .supported_shader_formats = PULSE_SHADER_FORMAT_SPIRV_BIT, + .driver_data = PULSE_NULLPTR }; diff --git a/Sources/Backends/Vulkan/Vulkan.h b/Sources/Backends/Vulkan/Vulkan.h index 0040b7d..bb4d526 100644 --- a/Sources/Backends/Vulkan/Vulkan.h +++ b/Sources/Backends/Vulkan/Vulkan.h @@ -12,6 +12,8 @@ #include "VulkanDevice.h" #include "VulkanInstance.h" +#define VULKAN_RETRIEVE_DRIVER_DATA(device) ((VulkanDriverData*)device->driver_data) + typedef struct VulkanGlobal { #define PULSE_VULKAN_GLOBAL_FUNCTION(fn) PFN_##fn fn; @@ -19,14 +21,16 @@ typedef struct VulkanGlobal #undef PULSE_VULKAN_GLOBAL_FUNCTION } VulkanGlobal; -typedef struct VulkanPulseDevice +typedef struct VulkanDriverData { VulkanInstance instance; VulkanDevice device; -} VulkanPulseDevice; +} VulkanDriverData; VulkanGlobal* VulkanGetGlobal(); +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_ENABLE_VULKAN_BACKEND diff --git a/Sources/Backends/Vulkan/VulkanDevice.c b/Sources/Backends/Vulkan/VulkanDevice.c index 772ccaf..92827c6 100644 --- a/Sources/Backends/Vulkan/VulkanDevice.c +++ b/Sources/Backends/Vulkan/VulkanDevice.c @@ -2,6 +2,99 @@ // This file is part of "Pulse" // For conditions of distribution and use, see copyright notice in LICENSE +#include "VulkanInstance.h" #include "VulkanDevice.h" +#include "../../PulseInternal.h" +/* +static int32_t VulkanScorePhysicalDevice(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)); + if(!props) + return -1; + kbhGetVulkanPFNs()->vkEnumerateDeviceExtensionProperties(device, PULSE_NULLPTR, &extension_count, props); + bool are_there_required_device_extensions = true; + for(int j = 0; j < device_extensions_count; j++) + { + bool is_there_extension = false; + for(int k = 0; k < extension_count; k++) + { + if(strcmp(device_extensions[j], props[k].extensionName) == 0) + { + is_there_extension = true; + break; + } + } + if(is_there_extension == false) + { + are_there_required_device_extensions = false; + break; + } + } + if(are_there_required_device_extensions == false) + return -1; + // Check Queue Families Support + int32_t queue; + if(kbhFindPhysicalDeviceQueueFamily(device, KBH_VULKAN_QUEUE_COMPUTE, &queue) != KBH_RHI_SUCCESS) + return -1; + + VkPhysicalDeviceProperties device_props; + kbhGetVulkanPFNs()->vkGetPhysicalDeviceProperties(device, &device_props); + + VkPhysicalDeviceFeatures device_features; + kbhGetVulkanPFNs()->vkGetPhysicalDeviceFeatures(device, &device_features); + + int32_t score = -1; + if(device_props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + score += 1001; + + score += device_props.limits.maxComputeWorkGroupCount[0]; + score += device_props.limits.maxComputeWorkGroupCount[1]; + score += device_props.limits.maxComputeWorkGroupCount[2]; + score += device_props.limits.maxComputeSharedMemorySize; + + return score; +} + +static VkPhysicalDevice VulkanPickPhysicalDevice(VulkanInstance* instance) +{ + VkPhysicalDevice* devices = PULSE_NULLPTR; + VkPhysicalDevice chosen_one = VK_NULL_HANDLE; + uint32_t device_count; + int32_t best_device_score = -1; + + instance->vkEnumeratePhysicalDevices(instance->instance, &device_count, PULSE_NULLPTR); + devices = (VkPhysicalDevice*)calloc(device_count, sizeof(VkPhysicalDevice)); + PULSE_CHECK_ALLOCATION_RETVAL(devices, VK_NULL_HANDLE); + instance->vkEnumeratePhysicalDevices(instance->instance, &device_count, devices); + + for(int i = 0; i < device_count; i++) + { + int32_t current_device_score = VulkanScorePhysicalDevice(devices[i], PULSE_NULLPTR, 0); + if(current_device_score > best_device_score) + { + best_device_score = current_device_score; + chosen_one = devices[i]; + } + } + return chosen_one; +} +*/ + +void* VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count) +{ +} + +void VulkanDestroyDevice(VulkanDevice* device) +{ + if(device == PULSE_NULLPTR || device->device == VK_NULL_HANDLE) + return; + vmaDestroyAllocator(device->allocator); + device->vkDestroyDevice(device->device, PULSE_NULLPTR); + device->device = VK_NULL_HANDLE; +} diff --git a/Sources/Backends/Vulkan/VulkanDevice.h b/Sources/Backends/Vulkan/VulkanDevice.h index 23be3cb..8bd3e81 100644 --- a/Sources/Backends/Vulkan/VulkanDevice.h +++ b/Sources/Backends/Vulkan/VulkanDevice.h @@ -32,7 +32,7 @@ typedef struct VulkanDevice #undef PULSE_VULKAN_DEVICE_FUNCTION } VulkanDevice; -VulkanDevice* VulkanCreateDevice(PulseDebugLevel debug_level); +void* VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count); void VulkanDestroyDevice(VulkanDevice* device); #endif // PULSE_VULKAN_DEVICE_H_ diff --git a/Sources/Backends/Vulkan/VulkanInstance.c b/Sources/Backends/Vulkan/VulkanInstance.c index c5e40c1..5740d5a 100644 --- a/Sources/Backends/Vulkan/VulkanInstance.c +++ b/Sources/Backends/Vulkan/VulkanInstance.c @@ -24,7 +24,10 @@ static VkInstance VulkanCreateInstance(const char** extensions_enabled, uint32_t create_info.pApplicationInfo = &app_info; create_info.enabledExtensionCount = extensions_count; create_info.ppEnabledExtensionNames = extensions_enabled; - create_info.enabledLayerCount = sizeof(layer_names) / sizeof(layer_names[0]); + if(debug_level == PULSE_NO_DEBUG) + create_info.enabledLayerCount = 0; + else + create_info.enabledLayerCount = sizeof(layer_names) / sizeof(layer_names[0]); create_info.ppEnabledLayerNames = layer_names; create_info.pNext = PULSE_NULLPTR; #ifdef PULSE_PLAT_MACOS @@ -39,17 +42,29 @@ static VkInstance VulkanCreateInstance(const char** extensions_enabled, uint32_t bool VulkanInitInstance(VulkanInstance* instance, PulseDebugLevel debug_level) { - instance->instance = VulkanCreateInstance(NULL, 0, debug_level); + #ifdef PULSE_PLAT_MACOS + const char* extensions[] = { + VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, + }; + #else + const char* extensions[] = { + }; + #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)) + if(!VulkanLoadInstance(instance)) return false; return true; } void VulkanDestroyInstance(VulkanInstance* instance) { + if(instance == PULSE_NULLPTR || instance->instance == VK_NULL_HANDLE) + return; + instance->vkDestroyInstance(instance->instance, PULSE_NULLPTR); + instance->instance = VK_NULL_HANDLE; } diff --git a/Sources/Backends/Vulkan/VulkanLoader.c b/Sources/Backends/Vulkan/VulkanLoader.c index 0bc4004..f1fcfa1 100644 --- a/Sources/Backends/Vulkan/VulkanLoader.c +++ b/Sources/Backends/Vulkan/VulkanLoader.c @@ -40,6 +40,8 @@ static LibModule vulkan_lib_module = PULSE_NULLPTR; +static uint32_t loader_references_count = 0; + static bool VulkanLoadGlobalFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)); static bool VulkanLoadInstanceFunctions(VulkanInstance* instance, PFN_vkVoidFunction (*load)(void*, const char*)); static bool VulkanLoadDeviceFunctions(VulkanInstance* instance, VulkanDevice* device, PFN_vkVoidFunction (*load)(VulkanInstance*, void*, const char*)); @@ -109,6 +111,9 @@ bool VulkanInitLoader() }; #endif + if(loader_references_count != 0) + return true; + for(size_t i = 0; i < sizeof(libnames) / sizeof(const char*); i++) { vulkan_lib_module = LoadLibrary(libnames[i]); @@ -127,6 +132,7 @@ bool VulkanInitLoader() PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); return false; } + loader_references_count++; return VulkanLoadGlobalFunctions(PULSE_NULLPTR, vkGetInstanceProcAddrStub); } @@ -142,6 +148,9 @@ bool VulkanLoadDevice(VulkanInstance* instance, VulkanDevice* device) void VulkanLoaderShutdown() { + loader_references_count--; + if(loader_references_count != 0) + return; UnloadLibrary(vulkan_lib_module); vulkan_lib_module = PULSE_NULLPTR; } diff --git a/Sources/PulseBackend.c b/Sources/PulseBackend.c new file mode 100644 index 0000000..58c6ca9 --- /dev/null +++ b/Sources/PulseBackend.c @@ -0,0 +1,118 @@ +// Copyright (C) 2024 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +#include +#include "PulseInternal.h" + +#ifdef PULSE_ENABLE_VULKAN_BACKEND + #include "Backends/Vulkan/Vulkan.h" +#endif +#ifdef PULSE_ENABLE_D3D11_BACKEND + #include "Backends/D3D11/D3D11.h" +#endif + +// Ordered by default preference +static const PulseCheckBackendSupportPFN backends_supports[] = { + #ifdef PULSE_ENABLE_VULKAN_BACKEND + VulkanCheckSupport, + #endif + #ifdef PULSE_ENABLE_D3D11_BACKEND + D3D11CheckSupport, + #endif + PULSE_NULLPTR +}; + +static PulseErrorType last_error = PULSE_ERROR_NONE; + +void PulseSetInternalError(PulseErrorType error) +{ + last_error = error; +} + +static PulseBackendFlags PulseSelectBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used) +{ + if((backend_candidates & PULSE_BACKEND_INVALID) != 0) + { + PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); + return PULSE_BACKEND_INVALID; + } + for(int i = 0; backends_supports[i] != PULSE_NULLPTR; i++) + { + PulseBackendFlags backend = backends_supports[i](backend_candidates, shader_formats_used); + if(backend != PULSE_BACKEND_INVALID) + return backend; + } + PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); + return PULSE_BACKEND_INVALID; +} + +static PulseBackend PulseGetBackendFromFlag(PulseBackendBits flag) +{ + switch(flag) + { + #ifdef PULSE_ENABLE_VULKAN_BACKEND + case PULSE_BACKEND_VULKAN: return &VulkanDriver; + #endif + #ifdef PULSE_ENABLE_D3D11_BACKEND + case PULSE_BACKEND_VULKAN: return &D3D11Driver; + #endif + + default: return PULSE_NULL_HANDLE; + } + return PULSE_NULL_HANDLE; // To avoid warnings +} + +PULSE_API PulseBackend PulseLoadBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used, PulseDebugLevel debug_level) +{ + PulseBackendFlags backend_type = PulseSelectBackend(backend_candidates, shader_formats_used); + if(backend_type == PULSE_BACKEND_INVALID) // Error code already set by PulseSelectBackend + return PULSE_NULL_HANDLE; + + PulseBackend backend = PulseGetBackendFromFlag(backend_type); + if(backend == PULSE_NULL_HANDLE) + return PULSE_NULL_HANDLE; + if(!backend->PFN_LoadBackend(debug_level)) + return PULSE_NULL_HANDLE; + return (PulseBackend)backend; +} + +PULSE_API void PulseUnloadBackend(PulseBackend backend) +{ + PULSE_CHECK_HANDLE(backend); + backend->PFN_UnloadBackend(backend); +} + +PULSE_API bool PulseSupportsBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used) +{ + if((backend_candidates & PULSE_BACKEND_INVALID) != 0) + return false; + for(int i = 0; backends_supports[i] != PULSE_NULLPTR; i++) + { + PulseBackendFlags backend = backends_supports[i](backend_candidates, shader_formats_used); + if(backend != PULSE_BACKEND_INVALID) + return true; + } + return false; +} + +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_ALLOCATION_FAILED: return "an internal allocation failed"; + + default: return "invalid error type"; + }; + return PULSE_NULLPTR; // To avoid warnings, should be unreachable +} diff --git a/Sources/PulseDevice.c b/Sources/PulseDevice.c index 17845d4..fdbeae3 100644 --- a/Sources/PulseDevice.c +++ b/Sources/PulseDevice.c @@ -5,65 +5,9 @@ #include #include "PulseInternal.h" -#define PULSE_CHECK_HANDLE_RETVAL(handle, retval) \ - do { \ - if(handle == PULSE_NULL_HANDLE) \ - { \ - PulseSetInternalError(PULSE_ERROR_INVALID_HANDLE); \ - return retval; \ - } \ - } while(0); \ - -#define PULSE_CHECK_HANDLE(handle) PULSE_CHECK_HANDLE_RETVAL(handle, ) - -// Ordered by default preference -static const PulseBackendLoader* backends[] = { - #ifdef PULSE_ENABLE_VULKAN_BACKEND - &VulkanDriver, - #endif - #ifdef PULSE_ENABLE_D3D11_BACKEND - &D3D11Driver, - #endif - PULSE_NULLPTR -}; - -static PulseErrorType last_error = PULSE_ERROR_NONE; - -void PulseSetInternalError(PulseErrorType error) +PULSE_API PulseDevice PulseCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count) { - last_error = error; -} - -static const PulseBackendLoader* PulseSelectBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used) -{ - if((backend_candidates & PULSE_BACKEND_INVALID) != 0) - { - PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); - return PULSE_NULLPTR; - } - for(int i = 0; backends[i] != PULSE_NULLPTR; i++) - { - if(backend_candidates != PULSE_BACKEND_ANY && (backend_candidates & backends[i]->backend) == 0) - continue; - if((shader_formats_used & backends[i]->supported_shader_formats) == 0) - { - PulseSetInternalError(PULSE_ERROR_BACKENDS_CANDIDATES_SHADER_FORMAT_MISMATCH); - return PULSE_NULLPTR; - } - if(backends[i]->PFN_LoadBackend()) - return backends[i]; - } - PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); - return PULSE_NULLPTR; -} - -PULSE_API PulseDevice PulseCreateDevice(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used, PulseDebugLevel debug_level) -{ - const PulseBackendLoader* backend = PulseSelectBackend(backend_candidates, shader_formats_used); - if(backend == PULSE_NULLPTR) // Error code already set by PulseSelectBackend - return PULSE_NULL_HANDLE; - - PulseDevice device = backend->PFN_CreateDevice(debug_level); + PulseDevice device = backend->PFN_CreateDevice(backend, forbiden_devices, forbiden_devices_count); if(device == PULSE_NULL_HANDLE) PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); device->backend = backend; @@ -73,7 +17,7 @@ PULSE_API PulseDevice PulseCreateDevice(PulseBackendFlags backend_candidates, Pu PULSE_API void PulseDestroyDevice(PulseDevice device) { PULSE_CHECK_HANDLE(device); - device->backend->PFN_DestroyDevice(device); + device->PFN_DestroyDevice(device); } PULSE_API PulseBackendBits PulseGetBackendInUseByDevice(PulseDevice device) @@ -82,45 +26,8 @@ PULSE_API PulseBackendBits PulseGetBackendInUseByDevice(PulseDevice device) return device->backend->backend; } -PULSE_API bool PulseSupportsBackend(PulseBackendFlags backend_candidates, PulseShaderFormatsFlags shader_formats_used) -{ - if((backend_candidates & PULSE_BACKEND_INVALID) != 0) - return false; - if((backend_candidates & PULSE_BACKEND_ANY) != 0) - return true; - for(int i = 0; backends[i] != PULSE_NULLPTR; i++) - { - if((backend_candidates & backends[i]->backend) == 0) - continue; - if((shader_formats_used & backends[i]->supported_shader_formats) != 0) - return true; - } - return false; -} - -PULSE_API bool PulseDeviceSupportsSahderFormats(PulseDevice device, PulseShaderFormatsFlags shader_formats_used) +PULSE_API bool PulseDeviceSupportsShaderFormats(PulseDevice device, PulseShaderFormatsFlags shader_formats_used) { PULSE_CHECK_HANDLE_RETVAL(device, false); return (device->backend->supported_shader_formats & shader_formats_used) != 0; } - -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_ALLOCATION_FAILED: return "an internal allocation failed"; - - default: return "invalid error type"; - }; - return PULSE_NULLPTR; // To avoid warnings, should be unreachable -} diff --git a/Sources/PulseInternal.h b/Sources/PulseInternal.h index 3c6e396..222f4fd 100644 --- a/Sources/PulseInternal.h +++ b/Sources/PulseInternal.h @@ -22,30 +22,46 @@ extern "C" { #define PULSE_CHECK_ALLOCATION(ptr) PULSE_CHECK_ALLOCATION_RETVAL(ptr, ) -typedef bool (*PulseLoadBackendPFN)(void); -typedef PulseDevice (*PulseCreateDevicePFN)(PulseDebugLevel); +#define PULSE_CHECK_HANDLE_RETVAL(handle, retval) \ + do { \ + if(handle == PULSE_NULL_HANDLE) \ + { \ + PulseSetInternalError(PULSE_ERROR_INVALID_HANDLE); \ + return retval; \ + } \ + } while(0); \ + +#define PULSE_CHECK_HANDLE(handle) PULSE_CHECK_HANDLE_RETVAL(handle, ) + +typedef PulseBackendFlags (*PulseCheckBackendSupportPFN)(PulseBackendFlags, PulseShaderFormatsFlags); + +typedef bool (*PulseLoadBackendPFN)(PulseDebugLevel); +typedef void (*PulseUnloadBackendPFN)(PulseBackend); +typedef void* (*PulseCreateDevicePFN)(PulseBackend, PulseDevice*, uint32_t); + typedef void (*PulseDestroyDevicePFN)(PulseDevice); -typedef struct PulseBackendLoader +typedef struct PulseBackendHandler { // PFNs PulseLoadBackendPFN PFN_LoadBackend; + PulseUnloadBackendPFN PFN_UnloadBackend; PulseCreateDevicePFN PFN_CreateDevice; - PulseDestroyDevicePFN PFN_DestroyDevice; // Attributes PulseBackendFlags backend; PulseShaderFormatsFlags supported_shader_formats; -} PulseBackendLoader; + void* driver_data; +} PulseBackendHandler; typedef struct PulseDeviceHandler { // PFNs + PulseDestroyDevicePFN PFN_DestroyDevice; // Attributes void* driver_data; - const PulseBackendLoader* backend; - PulseDebugLevel debug_level; + PulseBackend backend; } PulseDeviceHandler; void PulseSetInternalError(PulseErrorType error); @@ -55,10 +71,10 @@ void PulseSetInternalError(PulseErrorType error); PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \ #ifdef PULSE_ENABLE_VULKAN_BACKEND - extern PulseBackendLoader VulkanDriver; + extern PulseBackendHandler VulkanDriver; #endif // PULSE_ENABLE_VULKAN_BACKEND #ifdef PULSE_ENABLE_D3D11_BACKEND - extern PulseBackendLoader D3D11Driver; + extern PulseBackendHandler D3D11Driver; #endif // PULSE_ENABLE_D3D11_BACKEND #ifdef __cplusplus diff --git a/Tests/LoadingPulse/main.c b/Tests/LoadingPulse/main.c index 1478939..62d9959 100644 --- a/Tests/LoadingPulse/main.c +++ b/Tests/LoadingPulse/main.c @@ -4,12 +4,13 @@ int main(void) { - PulseDevice device = PulseCreateDevice(PULSE_BACKEND_ANY, PULSE_SHADER_FORMAT_SPIRV_BIT, PULSE_NO_DEBUG); - if(device == PULSE_NULL_HANDLE) + 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; } - PulseDestroyDevice(device); + PulseUnloadBackend(backend); + puts("Successfully loaded Pulse !"); return 0; }