diff --git a/Includes/Pulse.h b/Includes/Pulse.h index c582dfa..1d934d0 100644 --- a/Includes/Pulse.h +++ b/Includes/Pulse.h @@ -48,7 +48,6 @@ typedef enum PulseBufferUsageBits PULSE_BUFFER_USAGE_STORAGE_READ = PULSE_BIT(3), PULSE_BUFFER_USAGE_STORAGE_WRITE = PULSE_BIT(4), PULSE_BUFFER_USAGE_UNIFORM_ACCESS = PULSE_BIT(5), - PULSE_BUFFER_USAGE_HOST_ACCESS = PULSE_BIT(6), } PulseShaderFormatBits; typedef PulseFlags PulseBufferUsageFlags; @@ -190,6 +189,8 @@ typedef enum PulseImageFormat PULSE_IMAGE_FORMAT_BC2_RGBA_UNORM_SRGB, PULSE_IMAGE_FORMAT_BC3_RGBA_UNORM_SRGB, PULSE_IMAGE_FORMAT_BC7_RGBA_UNORM_SRGB, + + PULSE_IMAGE_FORMAT_MAX_ENUM // For internal use only } PulseImageFormat; // Structs @@ -275,6 +276,9 @@ PULSE_API PulseBuffer PulseCreateBuffer(PulseDevice device, const PulseBufferCre PULSE_API bool PulseGetBufferMap(PulseBuffer buffer, void** data); PULSE_API void PulseDestroyBuffer(PulseDevice device, PulseBuffer buffer); +PULSE_API PulseImage PulseCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos); +PULSE_API void PulseDestroyImage(PulseDevice device, PulseImage image); + PULSE_API PulseCommandList PulseRequestCommandList(PulseDevice device, PulseCommandListUsage usage); PULSE_API bool PulseSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence); PULSE_API void PulseReleaseCommandList(PulseDevice device, PulseCommandList cmd); diff --git a/Includes/PulseProfile.h b/Includes/PulseProfile.h index 2a9d32d..04db4a5 100644 --- a/Includes/PulseProfile.h +++ b/Includes/PulseProfile.h @@ -113,6 +113,8 @@ extern "C" { #else #define PULSE_NULLPTR NULL #endif +#elif defined(__cplusplus) && __cplusplus >= 201103L + #define PULSE_NULLPTR nullptr #else #define PULSE_NULLPTR NULL #endif diff --git a/Sources/Backends/D3D11/D3D11.c b/Sources/Backends/D3D11/D3D11.c index f0cb043..a9b8c55 100644 --- a/Sources/Backends/D3D11/D3D11.c +++ b/Sources/Backends/D3D11/D3D11.c @@ -3,9 +3,24 @@ // For conditions of distribution and use, see copyright notice in LICENSE #include +#include "../../PulseInternal.h" -#ifdef PULSE_ENABLE_D3D11_BACKEND +#include "D3D11.h" +PulseBackendFlags D3D11CheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used) +{ + if(candidates != PULSE_BACKEND_ANY && (candidates & PULSE_BACKEND_D3D11) == 0) + return PULSE_BACKEND_INVALID; + if((shader_formats_used & PULSE_SHADER_FORMAT_DXBC_BIT) == 0) + return PULSE_BACKEND_INVALID; + return PULSE_BACKEND_INVALID; // Not supported +} - -#endif // PULSE_ENABLE_D3D11_BACKEND +PulseBackendHandler D3D11Driver = { + .PFN_LoadBackend = PULSE_NULLPTR, + .PFN_UnloadBackend = PULSE_NULLPTR, + .PFN_CreateDevice = PULSE_NULLPTR, + .backend = PULSE_BACKEND_D3D11, + .supported_shader_formats = PULSE_SHADER_FORMAT_DXBC_BIT, + .driver_data = PULSE_NULLPTR +}; diff --git a/Sources/Backends/D3D11/D3D11.h b/Sources/Backends/D3D11/D3D11.h new file mode 100644 index 0000000..3c5c7dd --- /dev/null +++ b/Sources/Backends/D3D11/D3D11.h @@ -0,0 +1,16 @@ +// Copyright (C) 2024 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +#include + +#ifdef PULSE_ENABLE_D3D11_BACKEND + +#ifndef PULSE_D3D11_H_ +#define PULSE_D3D11_H_ + +PulseBackendFlags D3D11CheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used); // Return PULSE_BACKEND_VULKAN in case of success and PULSE_BACKEND_INVALID otherwise + +#endif // PULSE_D3D11_H_ + +#endif // PULSE_ENABLE_D3D11_BACKEND diff --git a/Sources/Backends/Vulkan/Vulkan.h b/Sources/Backends/Vulkan/Vulkan.h index b1bea2b..7d6ea2f 100644 --- a/Sources/Backends/Vulkan/Vulkan.h +++ b/Sources/Backends/Vulkan/Vulkan.h @@ -17,13 +17,16 @@ #define VULKAN_RETRIEVE_DRIVER_DATA_AS(handle, cast) ((cast)handle->driver_data) #define CHECK_VK_RETVAL(backend, res, error, retval) \ - 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); \ - return retval; \ - } + do { \ + 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); \ + return retval; \ + } \ + } while(0) \ + #define CHECK_VK(backend, res, error) CHECK_VK_RETVAL(backend, res, error, ) typedef struct VulkanGlobal diff --git a/Sources/Backends/Vulkan/VulkanBuffer.c b/Sources/Backends/Vulkan/VulkanBuffer.c index adb3cd2..37d185d 100644 --- a/Sources/Backends/Vulkan/VulkanBuffer.c +++ b/Sources/Backends/Vulkan/VulkanBuffer.c @@ -22,20 +22,26 @@ PulseBuffer VulkanCreateBuffer(PulseDevice device, const PulseBufferCreateInfo* buffer->size = create_infos->size; buffer->usage = create_infos->usage; - if(buffer->usage & PULSE_BUFFER_USAGE_TRANSFER_UPLOAD) - vulkan_buffer->usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - if(buffer->usage & PULSE_BUFFER_USAGE_TRANSFER_DOWNLOAD) - vulkan_buffer->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; - if(buffer->usage & PULSE_BUFFER_USAGE_STORAGE_READ || buffer->usage & PULSE_BUFFER_USAGE_STORAGE_WRITE) - vulkan_buffer->usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - if(buffer->usage & PULSE_BUFFER_USAGE_UNIFORM_ACCESS) - vulkan_buffer->usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - VmaAllocationCreateInfo allocation_create_info = { 0 }; allocation_create_info.usage = VMA_MEMORY_USAGE_AUTO; - if(buffer->usage & PULSE_BUFFER_USAGE_HOST_ACCESS) + if(buffer->usage & PULSE_BUFFER_USAGE_STORAGE_READ || buffer->usage & PULSE_BUFFER_USAGE_STORAGE_WRITE) + vulkan_buffer->usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if(buffer->usage & PULSE_BUFFER_USAGE_TRANSFER_UPLOAD) + { + vulkan_buffer->usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; allocation_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + if(buffer->usage & PULSE_BUFFER_USAGE_TRANSFER_DOWNLOAD) + { + vulkan_buffer->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + allocation_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + if(buffer->usage & PULSE_BUFFER_USAGE_UNIFORM_ACCESS) + { + vulkan_buffer->usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + allocation_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + } VkBufferCreateInfo buffer_create_info = { 0 }; buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -68,4 +74,6 @@ void VulkanDestroyBuffer(PulseDevice device, PulseBuffer buffer) VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); VulkanBuffer* vulkan_buffer = VULKAN_RETRIEVE_DRIVER_DATA_AS(buffer, VulkanBuffer*); vmaDestroyBuffer(vulkan_device->allocator, vulkan_buffer->buffer, vulkan_buffer->allocation); + free(vulkan_buffer); + free(buffer); } diff --git a/Sources/Backends/Vulkan/VulkanDescriptor.c b/Sources/Backends/Vulkan/VulkanDescriptor.c new file mode 100644 index 0000000..b0efafd --- /dev/null +++ b/Sources/Backends/Vulkan/VulkanDescriptor.c @@ -0,0 +1,183 @@ +// Copyright (C) 2024 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +#include + +#include "Pulse.h" +#include "PulseProfile.h" +#include "Vulkan.h" +#include "VulkanDevice.h" +#include "VulkanDescriptor.h" + +void VulkanInitDescriptorSetPool(VulkanDescriptorSetPool* pool, PulseDevice device) +{ + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); + memset(pool, 0, sizeof(VulkanDescriptorSetPool)); + pool->device = device; + + VkDescriptorPoolSize pool_sizes[ + PULSE_MAX_STORAGE_TEXTURES_BOUND + + PULSE_MAX_STORAGE_BUFFERS_BOUND + + PULSE_MAX_WRITE_TEXTURES_BOUND + + PULSE_MAX_WRITE_BUFFERS_BOUND + + PULSE_MAX_UNIFORM_BUFFERS_BOUND]; + + uint32_t i = 0; + + for(uint32_t start = i; i < start + PULSE_MAX_STORAGE_TEXTURES_BOUND; i++) + { + pool_sizes[i].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + pool_sizes[i].descriptorCount = VULKAN_POOL_SIZE; + } + + for(uint32_t start = i; i < start + PULSE_MAX_STORAGE_BUFFERS_BOUND; i++) + { + pool_sizes[i].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + pool_sizes[i].descriptorCount = VULKAN_POOL_SIZE; + } + + for(uint32_t start = i; i < start + PULSE_MAX_WRITE_TEXTURES_BOUND; i++) + { + pool_sizes[i].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + pool_sizes[i].descriptorCount = VULKAN_POOL_SIZE; + } + + for(uint32_t start = i; i < start + PULSE_MAX_WRITE_BUFFERS_BOUND; i++) + { + pool_sizes[i].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + pool_sizes[i].descriptorCount = VULKAN_POOL_SIZE; + } + + for(uint32_t start = i; i < start + PULSE_MAX_UNIFORM_BUFFERS_BOUND; i++) + { + pool_sizes[i].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + pool_sizes[i].descriptorCount = VULKAN_POOL_SIZE; + } + + VkDescriptorPoolCreateInfo pool_info = { 0 }; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.poolSizeCount = sizeof(pool_sizes) / sizeof(pool_sizes[0]); + pool_info.pPoolSizes = pool_sizes; + pool_info.maxSets = VULKAN_POOL_SIZE; + pool_info.flags = 0; + CHECK_VK(device->backend, vulkan_device->vkCreateDescriptorPool(vulkan_device->device, &pool_info, PULSE_NULLPTR, &pool->pool), PULSE_ERROR_INITIALIZATION_FAILED); +} + +VulkanDescriptorSet* VulkanRequestDescriptorSetFromPool(VulkanDescriptorSetPool* pool, const VulkanDescriptorSetLayout* layout) +{ + for(size_t i = 0; i < VULKAN_POOL_SIZE; i++) + { + if(pool->free_sets[i] == PULSE_NULLPTR) + break; // Due to defragmentation we are sure not to find valid sets after the first NULL one + if(pool->free_sets[i]->layout == layout) + { + VulkanDescriptorSet* set = pool->free_sets[i]; + PULSE_DEFRAG_ARRAY(pool->free_sets, VULKAN_POOL_SIZE, i); + pool->free_index--; + pool->used_sets[pool->used_index] = (VulkanDescriptorSet*)set; + pool->used_index++; + return set; + } + } + + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(pool->device, VulkanDevice*); + + VulkanDescriptorSet* set = (VulkanDescriptorSet*)calloc(1, sizeof(VulkanDescriptorSet)); + PULSE_CHECK_ALLOCATION_RETVAL(set, PULSE_NULLPTR); + + set->device = pool->device; + set->pool = pool; + set->layout = (VulkanDescriptorSetLayout*)layout; + + VkDescriptorSetAllocateInfo alloc_info = { 0 }; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = pool->pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &layout->layout; + CHECK_VK_RETVAL(pool->device->backend, vulkan_device->vkAllocateDescriptorSets(vulkan_device->device, &alloc_info, &set->set), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULLPTR); + + pool->allocations_count++; + return set; +} + +void VulkanReturnDescriptorSetToPool(VulkanDescriptorSetPool* pool, const VulkanDescriptorSet* set) +{ + for(size_t i = 0; i < VULKAN_POOL_SIZE; i++) + { + if(pool->used_sets[i] == PULSE_NULLPTR) + break; // Due to defragmentation we are sure not to find valid sets after the first NULL one + if(pool->used_sets[i] == set) + { + PULSE_DEFRAG_ARRAY(pool->used_sets, VULKAN_POOL_SIZE, i); + pool->used_index--; + pool->free_sets[pool->free_index] = (VulkanDescriptorSet*)set; + pool->free_index++; + return; + } + } +} + +void VulkanDestroyDescriptorSetPool(VulkanDescriptorSetPool* pool) +{ + if(pool->pool == VK_NULL_HANDLE) + return; + + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(pool->device, VulkanDevice*); + + for(size_t i = 0; i < VULKAN_POOL_SIZE; i++) + { + if(pool->used_sets[i] != PULSE_NULLPTR) + { + vulkan_device->vkDestroyDescriptorSetLayout(vulkan_device->device, pool->used_sets[i]->layout->layout, PULSE_NULLPTR); + free(pool->used_sets[i]); + pool->used_sets[i] = PULSE_NULLPTR; + } + if(pool->free_sets[i] != PULSE_NULLPTR) + { + vulkan_device->vkDestroyDescriptorSetLayout(vulkan_device->device, pool->free_sets[i]->layout->layout, PULSE_NULLPTR); + free(pool->free_sets[i]); + pool->free_sets[i] = PULSE_NULLPTR; + } + if(pool->free_sets[i] == PULSE_NULLPTR && pool->used_sets[i] == PULSE_NULLPTR) + break; // Due to defragmentation we are sure not to find valid sets after the first NULL one + } + vulkan_device->vkDestroyDescriptorPool(vulkan_device->device, pool->pool, PULSE_NULLPTR); + memset(pool, 0, sizeof(VulkanDescriptorSetPool)); +} + + +void VulkanInitDescriptorSetPoolManager(VulkanDescriptorSetPoolManager* manager, PulseDevice device) +{ + memset(manager, 0, sizeof(VulkanDescriptorSetPoolManager)); + manager->device = device; +} + +VulkanDescriptorSetPool* VulkanGetAvailableDescriptorSetPool(VulkanDescriptorSetPoolManager* manager) +{ + for(uint32_t i = 0; i < manager->pools_size; i++) + { + if(manager->pools[i]->allocations_count < VULKAN_POOL_SIZE || manager->pools[i]->free_sets[0] != PULSE_NULLPTR) + return manager->pools[i]; + } + PULSE_EXPAND_ARRAY_IF_NEEDED(manager->pools, VulkanDescriptorSetPool*, manager->pools_size, manager->pools_capacity, 1); + PULSE_CHECK_ALLOCATION_RETVAL(manager->pools, PULSE_NULLPTR); + + manager->pools[manager->pools_size] = (VulkanDescriptorSetPool*)calloc(1, sizeof(VulkanDescriptorSetPool)); + PULSE_CHECK_ALLOCATION_RETVAL(manager->pools[manager->pools_size], PULSE_NULLPTR); + + VulkanInitDescriptorSetPool(manager->pools[manager->pools_size], manager->device); + manager->pools_size++; + return manager->pools[manager->pools_size - 1]; +} + +void VulkanDestroyDescriptorSetPoolManager(VulkanDescriptorSetPoolManager* manager) +{ + for(uint32_t i = 0; i < manager->pools_size; i++) + { + VulkanDestroyDescriptorSetPool(manager->pools[i]); + free(manager->pools[i]); + } + free(manager->pools); + memset(manager, 0, sizeof(VulkanDescriptorSetPoolManager)); +} diff --git a/Sources/Backends/Vulkan/VulkanDescriptor.h b/Sources/Backends/Vulkan/VulkanDescriptor.h index 454741f..108c640 100644 --- a/Sources/Backends/Vulkan/VulkanDescriptor.h +++ b/Sources/Backends/Vulkan/VulkanDescriptor.h @@ -41,6 +41,7 @@ typedef struct VulkanDescriptorSetLayout typedef struct VulkanDescriptorSet { + PulseDevice device; VulkanDescriptorSetLayout* layout; struct VulkanDescriptorSetPool* pool; VkDescriptorSet set; @@ -50,18 +51,27 @@ typedef struct VulkanDescriptorSetPool { VulkanDescriptorSet* used_sets[VULKAN_POOL_SIZE]; VulkanDescriptorSet* free_sets[VULKAN_POOL_SIZE]; + uint32_t used_index; + uint32_t free_index; + PulseDevice device; VkDescriptorPool pool; - uint32_t sets_count; + uint32_t allocations_count; } VulkanDescriptorSetPool; typedef struct VulkanDescriptorSetPoolManager { - VulkanDescriptorSetPool* pools; + PulseDevice device; + VulkanDescriptorSetPool** pools; uint32_t pools_capacity; uint32_t pools_size; } VulkanDescriptorSetPoolManager; -void VulkanInitDescriptorSetPoolManager(VulkanDescriptorSetPoolManager* manager); +void VulkanInitDescriptorSetPool(VulkanDescriptorSetPool* pool, PulseDevice device); +VulkanDescriptorSet* VulkanRequestDescriptorSetFromPool(VulkanDescriptorSetPool* pool, const VulkanDescriptorSetLayout* layout); +void VulkanReturnDescriptorSetToPool(VulkanDescriptorSetPool* pool, const VulkanDescriptorSet* set); +void VulkanDestroyDescriptorSetPool(VulkanDescriptorSetPool* pool); + +void VulkanInitDescriptorSetPoolManager(VulkanDescriptorSetPoolManager* manager, PulseDevice device); VulkanDescriptorSetPool* VulkanGetAvailableDescriptorSetPool(VulkanDescriptorSetPoolManager* manager); void VulkanDestroyDescriptorSetPoolManager(VulkanDescriptorSetPoolManager* manager); diff --git a/Sources/Backends/Vulkan/VulkanDevice.c b/Sources/Backends/Vulkan/VulkanDevice.c index d806f8b..a78c80f 100644 --- a/Sources/Backends/Vulkan/VulkanDevice.c +++ b/Sources/Backends/Vulkan/VulkanDevice.c @@ -12,6 +12,7 @@ #include "VulkanLoader.h" #include "VulkanQueue.h" #include "VulkanBuffer.h" +#include "VulkanImage.h" #include "../../PulseInternal.h" #include diff --git a/Sources/Backends/Vulkan/VulkanDevicePrototypes.h b/Sources/Backends/Vulkan/VulkanDevicePrototypes.h index e9451cb..aced3c8 100644 --- a/Sources/Backends/Vulkan/VulkanDevicePrototypes.h +++ b/Sources/Backends/Vulkan/VulkanDevicePrototypes.h @@ -33,6 +33,7 @@ PULSE_VULKAN_DEVICE_FUNCTION(vkCreateDescriptorSetLayout) PULSE_VULKAN_DEVICE_FUNCTION(vkCreateFence) PULSE_VULKAN_DEVICE_FUNCTION(vkCreateImage) + PULSE_VULKAN_DEVICE_FUNCTION(vkCreateImageView) PULSE_VULKAN_DEVICE_FUNCTION(vkCreatePipelineCache) PULSE_VULKAN_DEVICE_FUNCTION(vkCreatePipelineLayout) PULSE_VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) @@ -45,6 +46,7 @@ PULSE_VULKAN_DEVICE_FUNCTION(vkDestroyDevice) PULSE_VULKAN_DEVICE_FUNCTION(vkDestroyFence) PULSE_VULKAN_DEVICE_FUNCTION(vkDestroyImage) + PULSE_VULKAN_DEVICE_FUNCTION(vkDestroyImageView) PULSE_VULKAN_DEVICE_FUNCTION(vkDestroyPipeline) PULSE_VULKAN_DEVICE_FUNCTION(vkDestroyPipelineCache) PULSE_VULKAN_DEVICE_FUNCTION(vkDestroyPipelineLayout) diff --git a/Sources/Backends/Vulkan/VulkanImage.c b/Sources/Backends/Vulkan/VulkanImage.c new file mode 100644 index 0000000..209e970 --- /dev/null +++ b/Sources/Backends/Vulkan/VulkanImage.c @@ -0,0 +1,191 @@ +// 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 "VulkanImage.h" +#include "VulkanDevice.h" + +static VkFormat PulseImageFormatToVkFormat[] = { + VK_FORMAT_UNDEFINED, // INVALID + VK_FORMAT_R8_UNORM, // A8_UNORM + VK_FORMAT_R8_UNORM, // R8_UNORM + VK_FORMAT_R8G8_UNORM, // R8G8_UNORM + VK_FORMAT_R8G8B8A8_UNORM, // R8G8B8A8_UNORM + VK_FORMAT_R16_UNORM, // R16_UNORM + VK_FORMAT_R16G16_UNORM, // R16G16_UNORM + VK_FORMAT_R16G16B16A16_UNORM, // R16G16B16A16_UNORM + VK_FORMAT_A2B10G10R10_UNORM_PACK32, // R10G10B10A2_UNORM + VK_FORMAT_R5G6B5_UNORM_PACK16, // B5G6R5_UNORM + VK_FORMAT_A1R5G5B5_UNORM_PACK16, // B5G5R5A1_UNORM + VK_FORMAT_B4G4R4A4_UNORM_PACK16, // B4G4R4A4_UNORM + VK_FORMAT_B8G8R8A8_UNORM, // B8G8R8A8_UNORM + VK_FORMAT_BC1_RGBA_UNORM_BLOCK, // BC1_UNORM + VK_FORMAT_BC2_UNORM_BLOCK, // BC2_UNORM + VK_FORMAT_BC3_UNORM_BLOCK, // BC3_UNORM + VK_FORMAT_BC4_UNORM_BLOCK, // BC4_UNORM + VK_FORMAT_BC5_UNORM_BLOCK, // BC5_UNORM + VK_FORMAT_BC7_UNORM_BLOCK, // BC7_UNORM + VK_FORMAT_BC6H_SFLOAT_BLOCK, // BC6H_FLOAT + VK_FORMAT_BC6H_UFLOAT_BLOCK, // BC6H_UFLOAT + VK_FORMAT_R8_SNORM, // R8_SNORM + VK_FORMAT_R8G8_SNORM, // R8G8_SNORM + VK_FORMAT_R8G8B8A8_SNORM, // R8G8B8A8_SNORM + VK_FORMAT_R16_SNORM, // R16_SNORM + VK_FORMAT_R16G16_SNORM, // R16G16_SNORM + VK_FORMAT_R16G16B16A16_SNORM, // R16G16B16A16_SNORM + VK_FORMAT_R16_SFLOAT, // R16_FLOAT + VK_FORMAT_R16G16_SFLOAT, // R16G16_FLOAT + VK_FORMAT_R16G16B16A16_SFLOAT, // R16G16B16A16_FLOAT + VK_FORMAT_R32_SFLOAT, // R32_FLOAT + VK_FORMAT_R32G32_SFLOAT, // R32G32_FLOAT + VK_FORMAT_R32G32B32A32_SFLOAT, // R32G32B32A32_FLOAT + VK_FORMAT_B10G11R11_UFLOAT_PACK32, // R11G11B10_UFLOAT + VK_FORMAT_R8_UINT, // R8_UINT + VK_FORMAT_R8G8_UINT, // R8G8_UINT + VK_FORMAT_R8G8B8A8_UINT, // R8G8B8A8_UINT + VK_FORMAT_R16_UINT, // R16_UINT + VK_FORMAT_R16G16_UINT, // R16G16_UINT + VK_FORMAT_R16G16B16A16_UINT, // R16G16B16A16_UINT + VK_FORMAT_R32_UINT, // R32_UINT + VK_FORMAT_R32G32_UINT, // R32G32_UINT + VK_FORMAT_R32G32B32A32_UINT, // R32G32B32A32_UINT + VK_FORMAT_R8_SINT, // R8_INT + VK_FORMAT_R8G8_SINT, // R8G8_INT + VK_FORMAT_R8G8B8A8_SINT, // R8G8B8A8_INT + VK_FORMAT_R16_SINT, // R16_INT + VK_FORMAT_R16G16_SINT, // R16G16_INT + VK_FORMAT_R16G16B16A16_SINT, // R16G16B16A16_INT + VK_FORMAT_R32_SINT, // R32_INT + VK_FORMAT_R32G32_SINT, // R32G32_INT + VK_FORMAT_R32G32B32A32_SINT, // R32G32B32A32_INT + VK_FORMAT_R8G8B8A8_SRGB, // R8G8B8A8_UNORM_SRGB + VK_FORMAT_B8G8R8A8_SRGB, // B8G8R8A8_UNORM_SRGB + VK_FORMAT_BC1_RGBA_SRGB_BLOCK, // BC1_UNORM_SRGB + VK_FORMAT_BC2_SRGB_BLOCK, // BC3_UNORM_SRGB + VK_FORMAT_BC3_SRGB_BLOCK, // BC3_UNORM_SRGB + VK_FORMAT_BC7_SRGB_BLOCK, // BC7_UNORM_SRGB +}; +PULSE_STATIC_ASSERT(PulseImageFormatToVkFormat, (sizeof(PulseImageFormatToVkFormat) / sizeof(VkFormat)) == PULSE_IMAGE_FORMAT_MAX_ENUM); + +static VkComponentMapping SwizzleForFormat(PulseImageFormat format) +{ + if(format == PULSE_IMAGE_FORMAT_A8_UNORM) + { + return (VkComponentMapping){ + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_R + }; + } + + if(format == PULSE_IMAGE_FORMAT_B4G4R4A4_UNORM) + { + return (VkComponentMapping){ + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_A, + VK_COMPONENT_SWIZZLE_B + }; + } + + return (VkComponentMapping){ + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY + }; +} + +PulseImage VulkanCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos) +{ + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); + + PulseImageHandler* image = (PulseImageHandler*)calloc(1, sizeof(PulseImageHandler)); + PULSE_CHECK_ALLOCATION_RETVAL(image, PULSE_NULL_HANDLE); + + VulkanImage* vulkan_image = (VulkanImage*)calloc(1, sizeof(VulkanImage)); + PULSE_CHECK_ALLOCATION_RETVAL(vulkan_image, PULSE_NULL_HANDLE); + + uint32_t layer_count = (create_infos->type == PULSE_IMAGE_TYPE_3D) ? 1 : create_infos->layer_count_or_depth; + uint32_t depth = (create_infos->type == PULSE_IMAGE_TYPE_3D) ? create_infos->layer_count_or_depth : 1; + + image->device = device; + image->driver_data = vulkan_image; + image->usage = create_infos->usage; + + VmaAllocationCreateInfo allocation_create_info = { 0 }; + allocation_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + + VkImageCreateFlags flags = 0; + + if(create_infos->type == PULSE_IMAGE_TYPE_CUBE || create_infos->type == PULSE_IMAGE_TYPE_CUBE_ARRAY) + flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + 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.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + 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); + + if(create_infos->usage & PULSE_IMAGE_USAGE_STORAGE_READ) + { + VkImageViewCreateInfo image_view_create_info = { 0 }; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.image = vulkan_image->image; + image_view_create_info.format = PulseImageFormatToVkFormat[create_infos->format]; + image_view_create_info.components = SwizzleForFormat(create_infos->format); + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = layer_count; + + if(create_infos->type == PULSE_IMAGE_TYPE_CUBE) + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + else if(create_infos->type == PULSE_IMAGE_TYPE_CUBE_ARRAY) + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; + else if(create_infos->type == PULSE_IMAGE_TYPE_3D) + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D; + else if(create_infos->type == PULSE_IMAGE_TYPE_2D_ARRAY) + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + else + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + + CHECK_VK_RETVAL(device->backend, vulkan_device->vkCreateImageView(vulkan_device->device, &image_view_create_info, PULSE_NULLPTR, &vulkan_image->view), PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE); + } + + return image; +} + +void VulkanDestroyImage(PulseDevice device, PulseImage image) +{ + VulkanDevice* vulkan_device = VULKAN_RETRIEVE_DRIVER_DATA_AS(device, VulkanDevice*); + VulkanImage* vulkan_image = VULKAN_RETRIEVE_DRIVER_DATA_AS(image, VulkanImage*); + vulkan_device->vkDestroyImageView(vulkan_device->device, vulkan_image->view, PULSE_NULLPTR); + vmaDestroyImage(vulkan_device->allocator, vulkan_image->image, vulkan_image->allocation); + free(vulkan_image); + free(image); +} diff --git a/Sources/Backends/Vulkan/VulkanImage.h b/Sources/Backends/Vulkan/VulkanImage.h new file mode 100644 index 0000000..a057fed --- /dev/null +++ b/Sources/Backends/Vulkan/VulkanImage.h @@ -0,0 +1,31 @@ +// Copyright (C) 2024 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +#ifdef PULSE_ENABLE_VULKAN_BACKEND + +#ifndef PULSE_VULKAN_IMAGE_H_ +#define PULSE_VULKAN_IMAGE_H_ + +#include +#include + +#include +#include "../../PulseInternal.h" +#include "VulkanEnums.h" + +typedef struct VulkanImage +{ + VkImage image; + VkImageView view; + VkImageUsageFlags usage; + VmaAllocation allocation; + VmaAllocationInfo allocation_info; +} VulkanImage; + +PulseImage VulkanCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos); +void VulkanDestroyImage(PulseDevice device, PulseImage image); + +#endif // PULSE_VULKAN_IMAGE_H_ + +#endif // PULSE_ENABLE_VULKAN_BACKEND diff --git a/Sources/PulseDefs.h b/Sources/PulseDefs.h index fd6c222..db3da4e 100644 --- a/Sources/PulseDefs.h +++ b/Sources/PulseDefs.h @@ -49,6 +49,33 @@ #define PULSE_CHECK_PTR(handle) PULSE_CHECK_PTR_RETVAL(handle, ) +#define PULSE_EXPAND_ARRAY_IF_NEEDED(array, T, size, capacity, increase) \ + do { \ + if(size >= capacity) \ + { \ + capacity += increase; \ + array = (T*)realloc(array, sizeof(T) * capacity); \ + } \ + } while(0); \ + +#define PULSE_DEFRAG_ARRAY(array, size, start) \ + for(size_t defrag_i = start; defrag_i < size - 1; defrag_i++) \ + array[defrag_i] = array[defrag_i + 1]; \ + +#ifndef PULSE_STATIC_ASSERT + #ifdef __cplusplus + #if __cplusplus >= 201103L + #define PULSE_STATIC_ASSERT(name, x) static_assert(x, #x) + #endif + #elif PULSE_C_VERSION >= 2023 + #define PULSE_STATIC_ASSERT(name, x) static_assert(x, #x) + #elif PULSE_C_VERSION >= 2011 + #define PULSE_STATIC_ASSERT(name, x) _Static_assert(x, #x) + #else + #define PULSE_STATIC_ASSERT(name, x) typedef int pulse_static_assert_##name[(x) ? 1 : -1] + #endif +#endif + #define PULSE_LOAD_DRIVER_DEVICE_FUNCTION(fn, _namespace) pulse_device->PFN_##fn = _namespace##fn; #define PULSE_LOAD_DRIVER_DEVICE(_namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \ @@ -65,5 +92,7 @@ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateBuffer, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(GetBufferMap, _namespace) \ PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyBuffer, _namespace) \ + PULSE_LOAD_DRIVER_DEVICE_FUNCTION(CreateImage, _namespace) \ + PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyImage, _namespace) \ #endif // PULSE_DEFS_H_ diff --git a/Sources/PulseImage.c b/Sources/PulseImage.c new file mode 100644 index 0000000..aac0a2c --- /dev/null +++ b/Sources/PulseImage.c @@ -0,0 +1,35 @@ +// 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 "PulseDefs.h" +#include "PulseInternal.h" + +PULSE_API PulseImage PulseCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos) +{ + PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE); + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) + { + if(create_infos == PULSE_NULLPTR) + { + PulseLogError(device->backend, "create_infos is NULL"); + PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED); + return PULSE_NULL_HANDLE; + } + } + return device->PFN_CreateImage(device, create_infos); +} + +PULSE_API void PulseDestroyImage(PulseDevice device, PulseImage image) +{ + PULSE_CHECK_HANDLE(device); + + if(image == PULSE_NULL_HANDLE) + { + if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend)) + PulseLogWarning(device->backend, "image is NULL, this may be a bug in your application"); + return; + } + return device->PFN_DestroyImage(device, image); +} diff --git a/Sources/PulseInternal.h b/Sources/PulseInternal.h index 5a6e4aa..eaa576a 100644 --- a/Sources/PulseInternal.h +++ b/Sources/PulseInternal.h @@ -72,6 +72,8 @@ typedef struct PulseDeviceHandler PulseCreateBufferPFN PFN_CreateBuffer; PulseGetBufferMapPFN PFN_GetBufferMap; PulseDestroyBufferPFN PFN_DestroyBuffer; + PulseCreateImagePFN PFN_CreateImage; + PulseDestroyImagePFN PFN_DestroyImage; // Attributes void* driver_data; @@ -86,7 +88,9 @@ typedef struct PulseFenceHandler typedef struct PulseImageHandler { + PulseDevice device; void* driver_data; + PulseImageUsageFlags usage; } PulseImageHandler; PulseThreadID PulseGetThreadID(); @@ -103,6 +107,12 @@ void PulseLogBackend(PulseBackend backend, PulseDebugMessageSeverity type, const #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__) +#define PULSE_MAX_STORAGE_TEXTURES_BOUND 8 +#define PULSE_MAX_STORAGE_BUFFERS_BOUND 8 +#define PULSE_MAX_UNIFORM_BUFFERS_BOUND 4 +#define PULSE_MAX_WRITE_TEXTURES_BOUND 8 +#define PULSE_MAX_WRITE_BUFFERS_BOUND 8 + #ifdef PULSE_ENABLE_VULKAN_BACKEND extern PulseBackendHandler VulkanDriver; #endif // PULSE_ENABLE_VULKAN_BACKEND diff --git a/Sources/PulsePFNs.h b/Sources/PulsePFNs.h index 6597739..f9a72a1 100644 --- a/Sources/PulsePFNs.h +++ b/Sources/PulsePFNs.h @@ -27,5 +27,7 @@ typedef void (*PulseReleaseCommandListPFN)(PulseDevice, PulseCommandList); typedef PulseBuffer (*PulseCreateBufferPFN)(PulseDevice, const PulseBufferCreateInfo*); typedef bool (*PulseGetBufferMapPFN)(PulseBuffer, void**); typedef void (*PulseDestroyBufferPFN)(PulseDevice, PulseBuffer); +typedef PulseImage (*PulseCreateImagePFN)(PulseDevice, const PulseImageCreateInfo*); +typedef void (*PulseDestroyImagePFN)(PulseDevice, PulseImage); #endif // PULSE_PFNS_H_ diff --git a/Tests/Vulkan/Buffer.c b/Tests/Vulkan/Buffer.c new file mode 100644 index 0000000..8fe00f5 --- /dev/null +++ b/Tests/Vulkan/Buffer.c @@ -0,0 +1,77 @@ +#include "Common.h" + +#include +#include + +void TestBufferCreation() +{ + PulseBackend backend; + SetupPulse(&backend); + PulseDevice device; + SetupDevice(backend, &device); + + PulseBufferCreateInfo buffer_create_info = { 0 }; + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_STORAGE_READ; + PulseBuffer buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_STORAGE_WRITE; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_TRANSFER_DOWNLOAD; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_TRANSFER_UPLOAD; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_UNIFORM_ACCESS; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_STORAGE_READ | PULSE_BUFFER_USAGE_STORAGE_WRITE; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_STORAGE_WRITE | PULSE_BUFFER_USAGE_TRANSFER_UPLOAD; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + buffer_create_info.size = 1024; + buffer_create_info.usage = PULSE_BUFFER_USAGE_STORAGE_READ | PULSE_BUFFER_USAGE_STORAGE_WRITE | PULSE_BUFFER_USAGE_TRANSFER_DOWNLOAD | PULSE_BUFFER_USAGE_TRANSFER_UPLOAD; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(buffer, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyBuffer(device, buffer); + + DISABLE_ERRORS; + buffer_create_info.size = -1; + buffer_create_info.usage = PULSE_BUFFER_USAGE_STORAGE_READ; + buffer = PulseCreateBuffer(device, &buffer_create_info); + TEST_ASSERT_EQUAL(buffer, PULSE_NULL_HANDLE); + PulseDestroyBuffer(device, buffer); + ENABLE_ERRORS; + + CleanupDevice(device); + CleanupPulse(backend); +} + +void TestBuffer() +{ + RUN_TEST(TestBufferCreation); +} diff --git a/Tests/Vulkan/Common.c b/Tests/Vulkan/Common.c index fe08d5b..2fa674e 100644 --- a/Tests/Vulkan/Common.c +++ b/Tests/Vulkan/Common.c @@ -1,9 +1,11 @@ #include "Common.h" #include +bool errors_enabled = true; + void DebugCallBack(PulseDebugMessageSeverity severity, const char* message) { - if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_ERROR) + if(errors_enabled && severity == PULSE_DEBUG_MESSAGE_SEVERITY_ERROR) TEST_FAIL_MESSAGE(message); } @@ -22,6 +24,17 @@ void SetupPulse(PulseBackend* backend) PulseSetDebugCallback(*backend, DebugCallBack); } +void SetupDevice(PulseBackend backend, PulseDevice* device) +{ + *device = PulseCreateDevice(backend, NULL, 0); + TEST_ASSERT_NOT_EQUAL_MESSAGE(device, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); +} + +void CleanupDevice(PulseDevice device) +{ + PulseDestroyDevice(device); +} + void CleanupPulse(PulseBackend backend) { PulseUnloadBackend(backend); diff --git a/Tests/Vulkan/Common.h b/Tests/Vulkan/Common.h index 5f0c70f..e11c3e5 100644 --- a/Tests/Vulkan/Common.h +++ b/Tests/Vulkan/Common.h @@ -19,8 +19,14 @@ #include +extern bool errors_enabled; +#define DISABLE_ERRORS errors_enabled = false +#define ENABLE_ERRORS errors_enabled = true + void DebugCallBack(PulseDebugMessageSeverity severity, const char* message); void SetupPulse(PulseBackend* backend); +void SetupDevice(PulseBackend backend, PulseDevice* device); +void CleanupDevice(PulseDevice device); void CleanupPulse(PulseBackend backend); #endif diff --git a/Tests/Vulkan/Image.c b/Tests/Vulkan/Image.c new file mode 100644 index 0000000..01dc06f --- /dev/null +++ b/Tests/Vulkan/Image.c @@ -0,0 +1,30 @@ +#include "Common.h" + +#include +#include + +void TestImageCreation() +{ + PulseBackend backend; + SetupPulse(&backend); + 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; + PulseImage image = PulseCreateImage(device, &image_create_info); + TEST_ASSERT_NOT_EQUAL_MESSAGE(image, PULSE_NULL_HANDLE, PulseVerbaliseErrorType(PulseGetLastErrorType())); + PulseDestroyImage(device, image); + + CleanupDevice(device); + CleanupPulse(backend); +} + +void TestImage() +{ + RUN_TEST(TestImageCreation); +} diff --git a/Tests/Vulkan/main.c b/Tests/Vulkan/main.c index e43f00d..83f1594 100644 --- a/Tests/Vulkan/main.c +++ b/Tests/Vulkan/main.c @@ -5,11 +5,15 @@ extern void TestBackend(); extern void TestDevice(); +extern void TestBuffer(); +extern void TestImage(); int main(void) { UNITY_BEGIN(); TestBackend(); TestDevice(); + TestBuffer(); + TestImage(); return UNITY_END(); }