mirror of
https://github.com/Kbz-8/Pulse.git
synced 2026-01-11 15:33:34 +00:00
fixing vulkan image creation issue, adding security to images creation, adding image unit tests
This commit is contained in:
@@ -233,7 +233,7 @@ typedef struct PulseImageCreateInfo
|
||||
PulseImageUsageFlags usage;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t layer_count_or_depth; // This value is treated as a layer count on 2D array textures, and as a depth value on 3D textures
|
||||
uint32_t layer_count_or_depth; // This value is treated as a layer count on 2D array images, and as a depth value on 3D images
|
||||
} PulseImageCreateInfo;
|
||||
|
||||
typedef struct PulseImageLocation
|
||||
@@ -278,6 +278,7 @@ PULSE_API void PulseUnmapBuffer(PulseBuffer buffer);
|
||||
PULSE_API void PulseDestroyBuffer(PulseDevice device, PulseBuffer buffer);
|
||||
|
||||
PULSE_API PulseImage PulseCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos);
|
||||
PULSE_API bool PulseIsImageFormatValid(PulseDevice device, PulseImageFormat format, PulseImageType type, PulseImageUsageFlags usage);
|
||||
PULSE_API void PulseDestroyImage(PulseDevice device, PulseImage image);
|
||||
|
||||
PULSE_API PulseCommandList PulseRequestCommandList(PulseDevice device, PulseCommandListUsage usage);
|
||||
|
||||
@@ -27,7 +27,7 @@ PulseBackendFlags VulkanCheckSupport(PulseBackendFlags candidates, PulseShaderFo
|
||||
if(!VulkanInitLoader())
|
||||
return PULSE_BACKEND_INVALID;
|
||||
VulkanInstance instance;
|
||||
if(!VulkanInitInstance(&instance, PULSE_NO_DEBUG)) // Instance creation will fail if Vulkan is not supported
|
||||
if(!VulkanInitInstance(PULSE_NULL_HANDLE, &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;
|
||||
@@ -37,13 +37,13 @@ PulseBackendFlags VulkanCheckSupport(PulseBackendFlags candidates, PulseShaderFo
|
||||
return PULSE_BACKEND_VULKAN;
|
||||
}
|
||||
|
||||
bool VulkanLoadBackend(PulseDebugLevel debug_level)
|
||||
bool VulkanLoadBackend(PulseBackend backend, 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))
|
||||
if(!VulkanInitInstance(backend, &driver_data->instance, debug_level))
|
||||
return false;
|
||||
VulkanDriver.driver_data = driver_data;
|
||||
return true;
|
||||
@@ -59,29 +59,29 @@ 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";
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
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)); \
|
||||
PulseLogErrorFmt(backend, "(Vulkan) call to a Vulkan function failed due to %s", VulkanVerbaliseResult(res)); \
|
||||
PulseSetInternalError(error); \
|
||||
return retval; \
|
||||
} \
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Vulkan.h"
|
||||
#include "VulkanImage.h"
|
||||
#include "VulkanDevice.h"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
static VkFormat PulseImageFormatToVkFormat[] = {
|
||||
VK_FORMAT_UNDEFINED, // INVALID
|
||||
@@ -126,26 +127,21 @@ PulseImage VulkanCreateImage(PulseDevice device, const PulseImageCreateInfo* cre
|
||||
else if(create_infos->type == PULSE_IMAGE_TYPE_3D)
|
||||
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||
|
||||
VkImageCreateInfo image_info = { 0 };
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.extent.width = create_infos->width;
|
||||
image_info.extent.height = create_infos->height;
|
||||
image_info.extent.depth = depth;
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = layer_count;
|
||||
image_info.format = PulseImageFormatToVkFormat[create_infos->format];
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.flags = flags;
|
||||
|
||||
VkImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
image_create_info.usage = vulkan_image->usage;
|
||||
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_create_info.extent.width = create_infos->width;
|
||||
image_create_info.extent.height = create_infos->height;
|
||||
image_create_info.extent.depth = depth;
|
||||
image_create_info.mipLevels = 1;
|
||||
image_create_info.arrayLayers = layer_count;
|
||||
image_create_info.format = PulseImageFormatToVkFormat[create_infos->format];
|
||||
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_create_info.flags = flags;
|
||||
|
||||
CHECK_VK_RETVAL(device->backend, vmaCreateImage(vulkan_device->allocator, &image_create_info, &allocation_create_info, &vulkan_image->image, &vulkan_image->allocation, PULSE_NULLPTR), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE);
|
||||
vmaGetAllocationInfo(vulkan_device->allocator, vulkan_image->allocation, &vulkan_image->allocation_info);
|
||||
@@ -180,6 +176,18 @@ PulseImage VulkanCreateImage(PulseDevice device, const PulseImageCreateInfo* cre
|
||||
return image;
|
||||
}
|
||||
|
||||
bool VulkanIsImageFormatValid(PulseDevice device, PulseImageFormat format, PulseImageType type, PulseImageUsageFlags usage)
|
||||
{
|
||||
(void)usage;
|
||||
VulkanDriverData* vulkan_driver_data = VULKAN_RETRIEVE_DRIVER_DATA_AS(device->backend, VulkanDriverData*);
|
||||
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
|
||||
VkImageCreateFlags vulkan_flags = 0;
|
||||
VkImageFormatProperties properties;
|
||||
if(type == PULSE_IMAGE_TYPE_CUBE || type == PULSE_IMAGE_TYPE_CUBE_ARRAY)
|
||||
vulkan_flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
return vulkan_driver_data->instance.vkGetPhysicalDeviceImageFormatProperties(vulkan_device->physical, PulseImageFormatToVkFormat[format], (type == PULSE_IMAGE_TYPE_3D) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_STORAGE_BIT, vulkan_flags, &properties) == VK_SUCCESS;
|
||||
}
|
||||
|
||||
void VulkanDestroyImage(PulseDevice device, PulseImage image)
|
||||
{
|
||||
VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*);
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef struct VulkanImage
|
||||
} VulkanImage;
|
||||
|
||||
PulseImage VulkanCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos);
|
||||
bool VulkanIsImageFormatValid(PulseDevice device, PulseImageFormat format, PulseImageType type, PulseImageUsageFlags usage);
|
||||
void VulkanDestroyImage(PulseDevice device, PulseImage image);
|
||||
|
||||
#endif // PULSE_VULKAN_IMAGE_H_
|
||||
|
||||
@@ -4,13 +4,104 @@
|
||||
|
||||
#include "../../PulseInternal.h"
|
||||
#include "VulkanInstance.h"
|
||||
#include "Pulse.h"
|
||||
#include "PulseProfile.h"
|
||||
#include "VulkanLoader.h"
|
||||
#include "Vulkan.h"
|
||||
|
||||
static VkInstance VulkanCreateInstance(const char** extensions_enabled, uint32_t extensions_count, PulseDebugLevel debug_level)
|
||||
{
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char* layer_names[] = { "VK_LAYER_KHRONOS_validation" };
|
||||
|
||||
static bool CheckValidationLayerSupport()
|
||||
{
|
||||
uint32_t layer_count;
|
||||
VulkanGetGlobal()->vkEnumerateInstanceLayerProperties(&layer_count, PULSE_NULLPTR);
|
||||
VkLayerProperties* available_layers = (VkLayerProperties*)calloc(layer_count, sizeof(VkLayerProperties));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(available_layers, false);
|
||||
VulkanGetGlobal()->vkEnumerateInstanceLayerProperties(&layer_count, available_layers);
|
||||
|
||||
for(size_t i = 0; i < sizeof(layer_names) / sizeof(layer_names[0]); i++)
|
||||
{
|
||||
bool found = false;
|
||||
for(size_t j = 0; j < layer_count; j++)
|
||||
{
|
||||
if(strcmp(available_layers[j].layerName, layer_names[i]) == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
{
|
||||
free(available_layers);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
free(available_layers);
|
||||
return true;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data)
|
||||
{
|
||||
(void)type;
|
||||
PulseBackend backend = user_data;
|
||||
if(severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT || severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
|
||||
PulseLogErrorFmt(backend, "(Vulkan) validation: %s", callback_data->pMessage);
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
static VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info, VkDebugUtilsMessengerEXT* messenger)
|
||||
{
|
||||
PFN_vkCreateDebugUtilsMessengerEXT fn = (PFN_vkCreateDebugUtilsMessengerEXT)VulkanGetGlobal()->vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
||||
if(fn)
|
||||
return fn(instance, create_info, NULL, messenger);
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
|
||||
static bool InitValidationLayers(PulseBackend backend, VulkanInstance* instance)
|
||||
{
|
||||
uint32_t extension_count;
|
||||
VulkanGetGlobal()->vkEnumerateInstanceExtensionProperties(PULSE_NULLPTR, &extension_count, NULL);
|
||||
VkExtensionProperties* extensions = (VkExtensionProperties*)calloc(extension_count, sizeof(VkExtensionProperties));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(extensions, false);
|
||||
VulkanGetGlobal()->vkEnumerateInstanceExtensionProperties(PULSE_NULLPTR, &extension_count, extensions);
|
||||
bool extension_found = false;
|
||||
for(uint32_t i = 0; i < extension_count; i++)
|
||||
{
|
||||
if(strcmp(extensions[i].extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0)
|
||||
{
|
||||
extension_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!extension_found)
|
||||
{
|
||||
fprintf(stderr, "Pulse: (Vulkan) " VK_EXT_DEBUG_UTILS_EXTENSION_NAME " is not present; cannot enable validation layers");
|
||||
free(extensions);
|
||||
return false;
|
||||
}
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT create_info = { 0 };
|
||||
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
create_info.pfnUserCallback = DebugCallback;
|
||||
create_info.pUserData = backend;
|
||||
CHECK_VK_RETVAL(backend, CreateDebugUtilsMessengerEXT(instance->instance, &create_info, &instance->debug_messenger), PULSE_ERROR_INITIALIZATION_FAILED, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void DestroyDebugUtilsMessengerEXT(VulkanInstance* instance)
|
||||
{
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT fn = (PFN_vkDestroyDebugUtilsMessengerEXT)VulkanGetGlobal()->vkGetInstanceProcAddr(instance->instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||
if(fn)
|
||||
fn(instance->instance, instance->debug_messenger, PULSE_NULLPTR);
|
||||
}
|
||||
|
||||
static VkInstance VulkanCreateInstance(PulseBackend backend, const char** extensions_enabled, uint32_t extensions_count, bool enable_validation_layers)
|
||||
{
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
|
||||
VkApplicationInfo app_info = {};
|
||||
@@ -19,28 +110,46 @@ static VkInstance VulkanCreateInstance(const char** extensions_enabled, uint32_t
|
||||
app_info.engineVersion = PULSE_VERSION;
|
||||
app_info.apiVersion = VK_API_VERSION_1_0;
|
||||
|
||||
VkInstanceCreateInfo create_info = {};
|
||||
VkInstanceCreateInfo create_info = { 0 };
|
||||
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
create_info.pApplicationInfo = &app_info;
|
||||
create_info.enabledExtensionCount = extensions_count;
|
||||
create_info.ppEnabledExtensionNames = extensions_enabled;
|
||||
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
|
||||
create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
#else
|
||||
create_info.flags = 0;
|
||||
#endif
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { 0 };
|
||||
const char** new_extension_set = PULSE_NULLPTR;
|
||||
if(enable_validation_layers)
|
||||
{
|
||||
new_extension_set = (const char**)calloc(extensions_count + 1, sizeof(char*));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(new_extension_set, PULSE_NULL_HANDLE);
|
||||
memcpy(new_extension_set, extensions_enabled, sizeof(char*) * extensions_count);
|
||||
new_extension_set[extensions_count] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
||||
|
||||
debug_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
debug_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
debug_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
debug_create_info.pfnUserCallback = DebugCallback;
|
||||
debug_create_info.pUserData = backend;
|
||||
|
||||
create_info.enabledExtensionCount = extensions_count + 1;
|
||||
create_info.ppEnabledExtensionNames = new_extension_set;
|
||||
create_info.enabledLayerCount = sizeof(layer_names) / sizeof(layer_names[0]);
|
||||
create_info.ppEnabledLayerNames = (const char* const*)layer_names;
|
||||
create_info.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debug_create_info;
|
||||
}
|
||||
|
||||
CHECK_VK_RETVAL(PULSE_NULL_HANDLE, VulkanGetGlobal()->vkCreateInstance(&create_info, PULSE_NULLPTR, &instance), PULSE_ERROR_INITIALIZATION_FAILED, VK_NULL_HANDLE);
|
||||
if(enable_validation_layers)
|
||||
free(new_extension_set);
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool VulkanInitInstance(VulkanInstance* instance, PulseDebugLevel debug_level)
|
||||
bool VulkanInitInstance(PulseBackend backend, VulkanInstance* instance, PulseDebugLevel debug_level)
|
||||
{
|
||||
#ifdef PULSE_PLAT_MACOS
|
||||
const char* extensions[] = {
|
||||
@@ -50,11 +159,14 @@ bool VulkanInitInstance(VulkanInstance* instance, PulseDebugLevel debug_level)
|
||||
const char* extensions[] = {
|
||||
};
|
||||
#endif
|
||||
instance->instance = VulkanCreateInstance(extensions, sizeof(extensions) / sizeof(char*), debug_level);
|
||||
instance->validation_layers_enabled = (backend != PULSE_NULL_HANDLE && debug_level == PULSE_HIGH_DEBUG && CheckValidationLayerSupport());
|
||||
instance->instance = VulkanCreateInstance(backend, extensions, sizeof(extensions) / sizeof(char*), instance->validation_layers_enabled);
|
||||
if(instance->instance == VK_NULL_HANDLE)
|
||||
return false;
|
||||
if(!VulkanLoadInstance(instance))
|
||||
return false;
|
||||
if(instance->validation_layers_enabled)
|
||||
InitValidationLayers(backend, instance);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,6 +174,8 @@ void VulkanDestroyInstance(VulkanInstance* instance)
|
||||
{
|
||||
if(instance == PULSE_NULLPTR || instance->instance == VK_NULL_HANDLE)
|
||||
return;
|
||||
if(instance->validation_layers_enabled)
|
||||
DestroyDebugUtilsMessengerEXT(instance);
|
||||
instance->vkDestroyInstance(instance->instance, PULSE_NULLPTR);
|
||||
instance->instance = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
@@ -13,14 +13,16 @@
|
||||
|
||||
typedef struct VulkanInstance
|
||||
{
|
||||
VkInstance instance;
|
||||
|
||||
#define PULSE_VULKAN_INSTANCE_FUNCTION(fn) PFN_##fn fn;
|
||||
#include "VulkanInstancePrototypes.h"
|
||||
#undef PULSE_VULKAN_INSTANCE_FUNCTION
|
||||
|
||||
VkInstance instance;
|
||||
VkDebugUtilsMessengerEXT debug_messenger;
|
||||
bool validation_layers_enabled;
|
||||
} VulkanInstance;
|
||||
|
||||
bool VulkanInitInstance(VulkanInstance* instance, PulseDebugLevel debug_level);
|
||||
bool VulkanInitInstance(PulseBackend backend, VulkanInstance* instance, PulseDebugLevel debug_level);
|
||||
void VulkanDestroyInstance(VulkanInstance* instance);
|
||||
|
||||
#endif // PULSE_VULKAN_INSTANCE_H_
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
|
||||
#define VMA_VULKAN_VERSION 1000000
|
||||
#define VMA_IMPLEMENTATION
|
||||
#define VMA_ASSERT_LEAK(expr) (void(0))
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
@@ -105,7 +105,7 @@ PULSE_API PulseBackend PulseLoadBackend(PulseBackendFlags backend_candidates, Pu
|
||||
PulseBackend backend = PulseGetBackendFromFlag(backend_type);
|
||||
if(backend == PULSE_NULL_HANDLE)
|
||||
return PULSE_NULL_HANDLE;
|
||||
if(!backend->PFN_LoadBackend(debug_level))
|
||||
if(!backend->PFN_LoadBackend(backend, debug_level))
|
||||
return PULSE_NULL_HANDLE;
|
||||
backend->PFN_UserDebugCallback = PULSE_NULLPTR;
|
||||
backend->debug_level = debug_level;
|
||||
|
||||
@@ -48,5 +48,10 @@ PULSE_API void PulseDestroyBuffer(PulseDevice device, PulseBuffer buffer)
|
||||
PulseLogWarning(device->backend, "buffer is NULL, this may be a bug in your application");
|
||||
return;
|
||||
}
|
||||
if(buffer->is_mapped)
|
||||
{
|
||||
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
|
||||
PulseLogWarning(device->backend, "buffer is still mapped, consider unmapping it before destroy");
|
||||
}
|
||||
return device->PFN_DestroyBuffer(device, buffer);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(UnmapBuffer, _namespace) \
|
||||
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyBuffer, _namespace) \
|
||||
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateImage, _namespace) \
|
||||
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(IsImageFormatValid, _namespace) \
|
||||
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyImage, _namespace) \
|
||||
|
||||
#endif // PULSE_DEFS_H_
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (C) 2024 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
// conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include "Pulse.h"
|
||||
#include "PulseDefs.h"
|
||||
@@ -17,10 +17,97 @@ PULSE_API PulseImage PulseCreateImage(PulseDevice device, const PulseImageCreate
|
||||
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
|
||||
return PULSE_NULL_HANDLE;
|
||||
}
|
||||
|
||||
bool failed = false;
|
||||
const uint32_t MAX_2D_DIMENSION = 16384;
|
||||
const uint32_t MAX_3D_DIMENSION = 2048;
|
||||
|
||||
if(create_infos->width <= 0 || create_infos->height <= 0 || create_infos->layer_count_or_depth <= 0)
|
||||
{
|
||||
PulseLogError(device->backend, "any image: width, height, and layer_count_or_depth must be >= 1");
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if(create_infos->type == PULSE_IMAGE_TYPE_CUBE)
|
||||
{
|
||||
if(create_infos->width != create_infos->height)
|
||||
{
|
||||
PulseLogError(device->backend, "cube images: width and height must be identical");
|
||||
failed = true;
|
||||
}
|
||||
if(create_infos->width > MAX_2D_DIMENSION || create_infos->height > MAX_2D_DIMENSION)
|
||||
{
|
||||
PulseLogError(device->backend, "cube images: width and height must be <= 16384");
|
||||
failed = true;
|
||||
}
|
||||
if(create_infos->layer_count_or_depth != 6)
|
||||
{
|
||||
PulseLogError(device->backend, "cube images: layer_count_or_depth must be 6");
|
||||
failed = true;
|
||||
}
|
||||
if(!PulseIsImageFormatValid(device, create_infos->format, PULSE_IMAGE_TYPE_CUBE, create_infos->usage))
|
||||
{
|
||||
PulseLogError(device->backend, "cube images: the format is unsupported for the given usage");
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
else if(create_infos->type == PULSE_IMAGE_TYPE_CUBE_ARRAY)
|
||||
{
|
||||
if(create_infos->width != create_infos->height)
|
||||
{
|
||||
PulseLogError(device->backend, "cube array images: width and height must be identical");
|
||||
failed = true;
|
||||
}
|
||||
if(create_infos->width > MAX_2D_DIMENSION || create_infos->height > MAX_2D_DIMENSION)
|
||||
{
|
||||
PulseLogError(device->backend, "cube array images: width and height must be <= 16384");
|
||||
failed = true;
|
||||
}
|
||||
if(create_infos->layer_count_or_depth % 6 != 0)
|
||||
{
|
||||
PulseLogError(device->backend, "cube array images: layer_count_or_depth must be a multiple of 6");
|
||||
failed = true;
|
||||
}
|
||||
if(!PulseIsImageFormatValid(device, create_infos->format, PULSE_IMAGE_TYPE_CUBE_ARRAY, create_infos->usage))
|
||||
{
|
||||
PulseLogError(device->backend, "cube array images: the format is unsupported for the given usage");
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
else if(create_infos->type == PULSE_IMAGE_TYPE_3D)
|
||||
{
|
||||
if(create_infos->width > MAX_3D_DIMENSION || create_infos->height > MAX_3D_DIMENSION || create_infos->layer_count_or_depth > MAX_3D_DIMENSION)
|
||||
{
|
||||
PulseLogError(device->backend, "3D images: width, height, and layer_count_or_depth must be <= 2048");
|
||||
failed = true;
|
||||
}
|
||||
if(!PulseIsImageFormatValid(device, create_infos->format, PULSE_IMAGE_TYPE_3D, create_infos->usage))
|
||||
{
|
||||
PulseLogError(device->backend, "3D images: the format is unsupported for the given usage");
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!PulseIsImageFormatValid(device, create_infos->format, PULSE_IMAGE_TYPE_2D, create_infos->usage))
|
||||
{
|
||||
PulseLogError(device->backend, "2D images: the format is unsupported for the given usage");
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(failed)
|
||||
return PULSE_NULL_HANDLE;
|
||||
}
|
||||
return device->PFN_CreateImage(device, create_infos);
|
||||
}
|
||||
|
||||
PULSE_API bool PulseIsImageFormatValid(PulseDevice device, PulseImageFormat format, PulseImageType type, PulseImageUsageFlags usage)
|
||||
{
|
||||
PULSE_CHECK_HANDLE_RETVAL(device, false);
|
||||
return device->PFN_IsImageFormatValid(device, format, type, usage);
|
||||
}
|
||||
|
||||
PULSE_API void PulseDestroyImage(PulseDevice device, PulseImage image)
|
||||
{
|
||||
PULSE_CHECK_HANDLE(device);
|
||||
|
||||
@@ -75,6 +75,7 @@ typedef struct PulseDeviceHandler
|
||||
PulseUnmapBufferPFN PFN_UnmapBuffer;
|
||||
PulseDestroyBufferPFN PFN_DestroyBuffer;
|
||||
PulseCreateImagePFN PFN_CreateImage;
|
||||
PulseIsImageFormatValidPFN PFN_IsImageFormatValid;
|
||||
PulseDestroyImagePFN PFN_DestroyImage;
|
||||
|
||||
// Attributes
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
typedef PulseBackendFlags (*PulseCheckBackendSupportPFN)(PulseBackendFlags, PulseShaderFormatsFlags);
|
||||
|
||||
typedef bool (*PulseLoadBackendPFN)(PulseDebugLevel);
|
||||
typedef bool (*PulseLoadBackendPFN)(PulseBackend, PulseDebugLevel);
|
||||
typedef void (*PulseUnloadBackendPFN)(PulseBackend);
|
||||
typedef PulseDevice (*PulseCreateDevicePFN)(PulseBackend, PulseDevice*, uint32_t);
|
||||
|
||||
@@ -29,6 +29,7 @@ typedef bool (*PulseMapBufferPFN)(PulseBuffer, void**);
|
||||
typedef void (*PulseUnmapBufferPFN)(PulseBuffer);
|
||||
typedef void (*PulseDestroyBufferPFN)(PulseDevice, PulseBuffer);
|
||||
typedef PulseImage (*PulseCreateImagePFN)(PulseDevice, const PulseImageCreateInfo*);
|
||||
typedef bool (*PulseIsImageFormatValidPFN)(PulseDevice, PulseImageFormat, PulseImageType, PulseImageUsageFlags);
|
||||
typedef void (*PulseDestroyImagePFN)(PulseDevice, PulseImage);
|
||||
|
||||
#endif // PULSE_PFNS_H_
|
||||
|
||||
@@ -102,7 +102,11 @@ void TestBufferMapping()
|
||||
PulseUnmapBuffer(buffer);
|
||||
}
|
||||
|
||||
DISABLE_ERRORS;
|
||||
void* ptr;
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(PulseMapBuffer(buffer, &ptr), false, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyBuffer(device, buffer);
|
||||
ENABLE_ERRORS;
|
||||
|
||||
CleanupDevice(device);
|
||||
CleanupPulse(backend);
|
||||
|
||||
@@ -10,15 +10,141 @@ void TestImageCreation()
|
||||
PulseDevice device;
|
||||
SetupDevice(backend, &device);
|
||||
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_2D;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_2D_ARRAY;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_3D;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_CUBE;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 6;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_CUBE_ARRAY;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 12;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_2D;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_WRITE;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_2D;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_SIMULTANEOUS_READWRITE;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_2D;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8_SNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType()));
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
|
||||
DISABLE_ERRORS;
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_2D;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 0;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_EQUAL(image, PULSE_NULL_HANDLE);
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_2D;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = -1;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_EQUAL(image, PULSE_NULL_HANDLE);
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
{
|
||||
PulseImageCreateInfo image_create_info = { 0 };
|
||||
image_create_info.type = PULSE_IMAGE_TYPE_CUBE;
|
||||
image_create_info.format = PULSE_IMAGE_FORMAT_R8G8B8A8_UNORM;
|
||||
image_create_info.usage = PULSE_IMAGE_USAGE_STORAGE_READ;
|
||||
image_create_info.width = 256;
|
||||
image_create_info.height = 256;
|
||||
image_create_info.layer_count_or_depth = 1;
|
||||
PulseImage image = PulseCreateImage(device, &image_create_info);
|
||||
TEST_ASSERT_EQUAL(image, PULSE_NULL_HANDLE);
|
||||
PulseDestroyImage(device, image);
|
||||
}
|
||||
ENABLE_ERRORS;
|
||||
|
||||
CleanupDevice(device);
|
||||
CleanupPulse(backend);
|
||||
|
||||
Reference in New Issue
Block a user