working on images, adding unit tests for image and buffers

This commit is contained in:
2024-12-08 03:16:10 +01:00
parent f020d8bf2c
commit 52850323fd
21 changed files with 697 additions and 25 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -3,9 +3,24 @@
// For conditions of distribution and use, see copyright notice in LICENSE
#include <Pulse.h>
#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
};

16
Sources/Backends/D3D11/D3D11.h git.filemode.normal_file
View File

@@ -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 <Pulse.h>
#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

View File

@@ -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

View File

@@ -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);
}

183
Sources/Backends/Vulkan/VulkanDescriptor.c git.filemode.normal_file
View File

@@ -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 <string.h>
#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));
}

View File

@@ -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);

View File

@@ -12,6 +12,7 @@
#include "VulkanLoader.h"
#include "VulkanQueue.h"
#include "VulkanBuffer.h"
#include "VulkanImage.h"
#include "../../PulseInternal.h"
#include <string.h>

View File

@@ -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)

191
Sources/Backends/Vulkan/VulkanImage.c git.filemode.normal_file
View File

@@ -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);
}

31
Sources/Backends/Vulkan/VulkanImage.h git.filemode.normal_file
View File

@@ -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 <vulkan/vulkan_core.h>
#include <vk_mem_alloc.h>
#include <Pulse.h>
#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

View File

@@ -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_

35
Sources/PulseImage.c git.filemode.normal_file
View File

@@ -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);
}

View File

@@ -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

View File

@@ -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_

77
Tests/Vulkan/Buffer.c git.filemode.normal_file
View File

@@ -0,0 +1,77 @@
#include "Common.h"
#include <unity/unity.h>
#include <Pulse.h>
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);
}

View File

@@ -1,9 +1,11 @@
#include "Common.h"
#include <unity/unity.h>
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);

View File

@@ -19,8 +19,14 @@
#include <Pulse.h>
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

30
Tests/Vulkan/Image.c git.filemode.normal_file
View File

@@ -0,0 +1,30 @@
#include "Common.h"
#include <unity/unity.h>
#include <Pulse.h>
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);
}

View File

@@ -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();
}