mirror of
https://github.com/Kbz-8/Pulse.git
synced 2026-01-11 15:33:34 +00:00
adding backend management responsability to the user
This commit is contained in:
@@ -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);
|
||||
|
||||
3
README.md
git.filemode.normal_file
3
README.md
git.filemode.normal_file
@@ -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.
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
118
Sources/PulseBackend.c
git.filemode.normal_file
118
Sources/PulseBackend.c
git.filemode.normal_file
@@ -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 <Pulse.h>
|
||||
#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
|
||||
}
|
||||
@@ -5,65 +5,9 @@
|
||||
#include <Pulse.h>
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user