summaryrefslogtreecommitdiff
path: root/unused code
diff options
context:
space:
mode:
authorVasco <vasco.guilherme.alves@gmail.com>2026-08-12 17:47:50 +0100
committerVasco <vasco.guilherme.alves@gmail.com>2026-08-12 17:47:50 +0100
commit95e3e76fd9c789cbf2d4409ef49456addc71643f (patch)
tree34161614f5f0ae909f58eb5e58a2ffd26c2f0e9f /unused code
rend 1.0.1HEADmain
Diffstat (limited to 'unused code')
-rw-r--r--unused code/rend_vk_buffer.c116
-rw-r--r--unused code/rend_vk_command_queue.c153
-rw-r--r--unused code/rend_vk_memory.c100
-rw-r--r--unused code/rend_vk_pool.c221
-rw-r--r--unused code/rend_vk_sbta.c331
5 files changed, 921 insertions, 0 deletions
diff --git a/unused code/rend_vk_buffer.c b/unused code/rend_vk_buffer.c
new file mode 100644
index 0000000..37f3e06
--- /dev/null
+++ b/unused code/rend_vk_buffer.c
@@ -0,0 +1,116 @@
+#pragma once
+#include "rend_internal.h"
+#include "rend_vk_internal.h"
+#include <vulkan/vulkan_core.h>
+
+#if 0
+static RendBuffer
+rend_vk_buffer_create(VkDevice logical_device, VkAllocationCallbacks *allocator, VkDeviceSize size, VkBufferUsageFlags usage, uint32_t *family_indices, uint32_t family_indices_count)
+{
+ RendBuffer buffer = {
+ .handle = 0,
+ .logical_device = logical_device,
+ .memory = NULL,
+ .allocator = allocator,
+ .usage = usage | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+ };
+
+ VkBufferCreateInfo buffer_info = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .size = size,
+ .usage = usage,
+ .sharingMode = (family_indices_count > 1) ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE,
+ .queueFamilyIndexCount = family_indices_count,
+ .pQueueFamilyIndices = family_indices
+ };
+
+ vkCreateBuffer(logical_device, &buffer_info, allocator, &buffer.handle);
+ return buffer;
+}
+
+static uint32_t
+rend_vk_buffer_required_memory_type(RendBuffer *buffer)
+{
+ assert(buffer->memory == NULL && "Buffer already bound to memory");
+ VkMemoryRequirements mem_requirements;
+ vkGetBufferMemoryRequirements(buffer->logical_device, buffer->handle, &mem_requirements);
+ return mem_requirements.memoryTypeBits;
+}
+
+static void
+rend_vk_buffer_bind_memory(RendBuffer *buffer, RendVkMemory *memory)
+{
+ assert(buffer->memory == NULL && "Buffer already bound to memory");
+ vkBindBufferMemory(buffer->logical_device, buffer->handle, memory->device_memory, 0);
+ buffer->memory = memory;
+}
+
+static void
+rend_vk_buffer_destroy(RendBuffer *buffer)
+{
+ assert(buffer && buffer->handle != 0);
+ vkDestroyBuffer(buffer->logical_device, buffer->handle, buffer->allocator);
+ buffer->handle = 0;
+ buffer->memory = 0;
+}
+
+static void
+rend_vk_buffer_copy_device(VkQueue queue, VkCommandPool pool, RendBuffer *dest, size_t dest_offset, size_t bytes, RendBuffer *src, size_t src_offset, VkFence fence)
+{
+ assert(src && dest); // check that im not sending null pointers
+ assert(src->usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT); // source buffer must be marked as transfer src
+ assert(dest->usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT); // dest buffer must be marked as transfer dest
+
+ VkCommandBuffer transfer_cmd = VK_NULL_HANDLE;
+
+ VkCommandBufferAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .commandBufferCount = 1,
+ .commandPool = pool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .pNext = NULL,
+ };
+
+ vkAllocateCommandBuffers(dest->logical_device, &alloc_info, &transfer_cmd);
+
+ VkCommandBufferBeginInfo begin_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+ vkBeginCommandBuffer(transfer_cmd, &begin_info);
+
+ VkBufferCopy buffer_copy = {
+ .srcOffset = (VkDeviceSize)src_offset,
+ .dstOffset = (VkDeviceSize)dest_offset,
+ .size = (VkDeviceSize)bytes,
+ };
+
+ vkCmdCopyBuffer(transfer_cmd, src->handle, dest->handle, 1, &buffer_copy);
+ vkEndCommandBuffer(transfer_cmd);
+
+ VkCommandBufferSubmitInfo cmd_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
+ .commandBuffer = transfer_cmd,
+ .deviceMask = 0,
+ };
+
+ VkSubmitInfo2 submit_info = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
+ .commandBufferInfoCount = 1,
+ .pCommandBufferInfos = &cmd_info,
+ };
+
+ vkQueueSubmit2(queue, 1, &submit_info, fence);
+ vkQueueWaitIdle(queue);
+ vkFreeCommandBuffers(dest->logical_device, pool, 1, &transfer_cmd);
+}
+
+static uint64_t
+rend_vk_buffer_address(RendBuffer *buffer, VkDevice device)
+{
+ VkBufferDeviceAddressInfoKHR address_info = {VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR};
+ address_info.buffer = buffer->handle;
+ VkDeviceAddress address = vkGetBufferDeviceAddress(device, &address_info);
+ return (uint64_t) address;
+}
+#endif
diff --git a/unused code/rend_vk_command_queue.c b/unused code/rend_vk_command_queue.c
new file mode 100644
index 0000000..1ddd780
--- /dev/null
+++ b/unused code/rend_vk_command_queue.c
@@ -0,0 +1,153 @@
+#pragma once
+#include "rend_internal.h"
+#include "rend_vk_internal.h"
+#include <vulkan/vulkan_core.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+
+/* DEFERRED COMMANDS:
+ * Tracks single-use command buffers submitted for async GPU work,
+ * so callers don't have to block (vkQueueWaitIdle) to know when it's
+ * safe to free/reset them. One shared timeline semaphore; every
+ * submission claims the next monotonic value.
+ */
+
+struct RendVkPendingCmd {
+ VkCommandPool pool;
+ VkCommandBuffer cmd;
+ uint64_t wait_value;
+};
+
+struct RendVkDeferredCmds {
+ VkSemaphore timeline;
+ RendVkPendingCmd *darray;
+ size_t count;
+ size_t capacity;
+};
+
+#define RENDER_VK_DEFERRED_CMDS_INITIAL_CAPACITY 16
+
+static inline RendVkDeferredCmds
+rend_vk_cmdbuf_deferred_create(void)
+{
+ RendVkDeferredCmds tracker = {0};
+
+ VkSemaphoreTypeCreateInfo type_info = {
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
+ .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
+ .initialValue = 0,
+ };
+ VkSemaphoreCreateInfo sem_info = {
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ .pNext = &type_info,
+ };
+ vkCreateSemaphore(vk_device.logical_device, &sem_info, vk_allocator, &tracker.timeline);
+
+ tracker.capacity = RENDER_VK_DEFERRED_CMDS_INITIAL_CAPACITY;
+ tracker.darray = rmalloc(sizeof(RendVkPendingCmd) * tracker.capacity);
+ tracker.count = 0;
+
+ return tracker;
+}
+
+static inline void
+rend_vk_cmdbuf_deferred_destroy(RendVkDeferredCmds *tracker)
+{
+ vkDestroySemaphore(vk_device.logical_device, tracker->timeline, vk_allocator);
+ rfree(tracker->darray);
+ *tracker = (RendVkDeferredCmds) {0};
+}
+
+static inline void rend_vk_cmdbuf_deferred_lock(RendVkDeferredCmds *t) { (void)t; }
+static inline void rend_vk_cmdbuf_deferred_unlock(RendVkDeferredCmds *t) { (void)t; }
+
+static inline VkCommandBuffer
+rend_vk_cmdbuf_deferred_begin(VkCommandPool pool)
+{
+ VkCommandBufferAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandPool = pool,
+ .commandBufferCount = 1,
+ };
+
+ VkCommandBuffer cmd;
+ vkAllocateCommandBuffers(vk_device.logical_device, &alloc_info, &cmd);
+
+ VkCommandBufferBeginInfo begin = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+
+ vkBeginCommandBuffer(cmd, &begin);
+ return cmd;
+}
+
+static inline void
+rend_vk_cmdbuf_deferred_push(RendVkDeferredCmds *tracker, VkCommandPool pool, VkCommandBuffer cmd, uint64_t wait_value)
+{
+ rend_vk_cmdbuf_deferred_lock(tracker);
+ if (tracker->count == tracker->capacity) {
+ tracker->capacity *= 2;
+ tracker->darray = realloc(tracker->darray, sizeof(RendVkPendingCmd) * tracker->capacity);
+ }
+ tracker->darray[tracker->count++] = (RendVkPendingCmd) {
+ .pool = pool,
+ .cmd = cmd,
+ .wait_value = wait_value,
+ };
+ rend_vk_cmdbuf_deferred_unlock(tracker);
+}
+
+static inline uint64_t
+rend_vk_cmdbuf_deferred_submit(RendVkDeferredCmds *tracker, VkCommandPool pool, VkCommandBuffer cmd, VkQueue queue, uint64_t wait_value, VkPipelineStageFlags2 wait_stage, VkPipelineStageFlags2 signal_stage)
+{
+ uint64_t signal_value = wait_value + 1;
+
+ VkCommandBufferSubmitInfo cmd_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
+ .commandBuffer = cmd,
+ };
+ VkSemaphoreSubmitInfo wait_info = {
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
+ .semaphore = tracker->timeline,
+ .value = wait_value,
+ .stageMask = wait_stage,
+ };
+ VkSemaphoreSubmitInfo signal_info = {
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
+ .semaphore = tracker->timeline,
+ .value = signal_value,
+ .stageMask = signal_stage,
+ };
+ VkSubmitInfo2 submit = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
+ .waitSemaphoreInfoCount = wait_value ? 1 : 0,
+ .pWaitSemaphoreInfos = &wait_info,
+ .commandBufferInfoCount = 1,
+ .pCommandBufferInfos = &cmd_info,
+ .signalSemaphoreInfoCount = 1,
+ .pSignalSemaphoreInfos = &signal_info,
+ };
+
+ vkQueueSubmit2(queue, 1, &submit, VK_NULL_HANDLE);
+
+ rend_vk_cmdbuf_deferred_push(tracker, pool, cmd, signal_value);
+ return signal_value;
+}
+
+static inline void
+rend_vk_cmdbuf_deferred_flush(RendVkDeferredCmds *tracker)
+{
+ uint64_t completed;
+ vkGetSemaphoreCounterValue(vk_device.logical_device, tracker->timeline, &completed);
+
+ for (size_t i = 0; i < tracker->count; ) {
+ if (completed >= tracker->darray[i].wait_value) {
+ vkFreeCommandBuffers(vk_device.logical_device, tracker->darray[i].pool, 1, &tracker->darray[i].cmd);
+ tracker->darray[i] = tracker->darray[--tracker->count];
+ } else {
+ i++;
+ }
+ }
+}
diff --git a/unused code/rend_vk_memory.c b/unused code/rend_vk_memory.c
new file mode 100644
index 0000000..4782b46
--- /dev/null
+++ b/unused code/rend_vk_memory.c
@@ -0,0 +1,100 @@
+#pragma once
+
+#include "rend_internal.h"
+#include "rend_vk_internal.h"
+#include <vulkan/vulkan_core.h>
+
+
+#if 0
+static RendVkMemory
+rend_vk_memory_malloc(size_t size, VkDevice logical_device, VkPhysicalDevice physical_device, uint32_t heap_index, VkAllocationCallbacks *allocator)
+{
+ RendVkMemory memory = {
+ .host_mapped_memory = NULL,
+ .device_memory = 0,
+ .logical_device = logical_device,
+ .heap_index = heap_index
+ };
+
+ VkMemoryAllocateFlagsInfo flags_info = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
+ .pNext = NULL,
+ .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, /* REQUIRED for BDA buffers */
+ .deviceMask = 0
+ };
+
+ VkMemoryAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = size,
+ .pNext = &flags_info,
+ .memoryTypeIndex = heap_index
+ };
+
+ vkAllocateMemory(logical_device, &alloc_info, allocator, &memory.device_memory);
+ return memory;
+}
+
+static void
+rend_vk_memory_free(RendVkMemory *memory)
+{
+ if (memory->host_mapped_memory) {
+ rend_vk_memory_unmap(memory);
+ }
+ if (memory->offset == 0) {
+ vkFreeMemory(memory->logical_device, memory->device_memory, memory->allocator);
+ memset(memory, 0, sizeof *memory);
+ } else {
+ PWARN("[REND_VK] Attempted to free memory with an offset!");
+ }
+}
+
+static void*
+rend_vk_memory_map(RendVkMemory *memory)
+{
+ // SPEC: memory must have been created with a memory type that reports VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
+ // vkMapMemory will fail if the implementation is unable to allocate an appropriately sized contiguous virtual address range,
+ // e.g. due to virtual address space fragmentation or platform limits.
+ // In such cases, vkMapMemory must return VK_ERROR_MEMORY_MAP_FAILED.
+ // The application can improve the likelihood of success by reducing the size of the mapped range and/or removing unneeded mappings using vkUnmapMemory.
+
+ vkMapMemory(memory->logical_device, memory->device_memory, memory->offset, memory->size, 0, &memory->host_mapped_memory);
+ return memory->host_mapped_memory;
+}
+
+static void
+rend_vk_memory_unmap(RendVkMemory *memory)
+{
+ if (memory->offset == 0) {
+ vkUnmapMemory(memory->logical_device, memory->device_memory);
+ memory->host_mapped_memory = 0;
+ } else {
+ PWARN("[REND_VK] Attempted to unmap memory with an offset!");
+ }
+}
+
+static void
+rend_vk_memory_copy(RendVkMemory *memory, size_t offset, const void *data, size_t size)
+{
+ /* bounds check */
+ if (offset + size > memory->size) {
+ REND__WARN("Memory write out of bounds!");
+ return;
+ }
+
+ /* important to cast this */
+ uint8_t *dest = memory->host_mapped_memory;
+ memcpy(dest + offset, data, size);
+
+ /* flush host writes if memory is non-coherent */
+ // VkMappedMemoryRange range = {
+ // .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
+ // .memory = memory->device_memory,
+ // .offset = offset,
+ // .size = size
+ // };
+ //
+ // vkFlushMappedMemoryRanges(memory->logical_device, 1, &range);
+
+}
+
+#endif
diff --git a/unused code/rend_vk_pool.c b/unused code/rend_vk_pool.c
new file mode 100644
index 0000000..e527354
--- /dev/null
+++ b/unused code/rend_vk_pool.c
@@ -0,0 +1,221 @@
+#pragma once
+#include "rend_internal.h"
+#include "rend_vk_internal.h"
+#include <vulkan/vulkan_core.h>
+
+/* NOTE(vasco):
+ * Check out https://kylehalladay.com/blog/tutorial/2017/12/13/Custom-Allocators-Vulkan.html
+ * for a basic grasp on what it entails to actually write a memory allocator for vulkan.
+ *
+ * This allocator isnt anything particularly amazing but it works so I'm keeping this code.
+ * Based on the framework presented here we could implement other more efficient allocators.
+ * Which is what I did for the arena allocator.
+ */
+
+#if 0
+typedef struct RendVkAddress {
+ VkDeviceMemory handle;
+ uint32_t type;
+ uint32_t id;
+ VkDeviceSize size;
+ VkDeviceSize offset;
+} RendVkAddress;
+
+typedef struct RendVkPoolLayout {
+ uint64_t offset, size;
+} RendVkPoolLayout;
+
+typedef struct RendVKPoolBlock {
+ RendVkAddress address;
+ RendVkPoolLayout *layout_darr;
+ uint8_t reserved;
+} RendVKPoolBlock;
+
+typedef struct RendVkPoolMemory {
+ RendVKPoolBlock *block_darr;
+} RendVkPoolMemory;
+
+struct RendVkPoolAllocator {
+ VkAllocationCallbacks *allocator;
+ size_t *mem_type_alloc_sizes; // allocation size per type of memory available on the gpu
+ RendVkPoolMemory *mem_pools; // memory pools per type of memory available on the gpu
+ VkDevice logical_device; // virtual device
+ VkPhysicalDevice physical_device; // physical device we are allocating memory from
+ VkDeviceSize page_size; // allocations must respect the physical limitations of the gpu
+ VkDeviceSize block_min_size; // minimum size per block of memory
+ uint64_t total_allocations;
+ uint32_t memory_type_count;
+};
+
+static RendVkPoolAllocator
+rend_vk_pool_create(VkDevice logical_device, VkPhysicalDevice physical_device, VkPhysicalDeviceLimits device_limits, VkAllocationCallbacks *allocator)
+{
+ RendVkPoolAllocator pool = {
+ .allocator = allocator,
+ .mem_type_alloc_sizes = NULL,
+ .mem_pools = NULL,
+ .logical_device = logical_device,
+ .physical_device = physical_device,
+ .page_size = 0,
+ .block_min_size = 0,
+ .total_allocations = 0,
+ .memory_type_count = 0,
+ };
+
+ VkPhysicalDeviceMemoryProperties mem_properties;
+ vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties);
+
+ pool.mem_type_alloc_sizes = malloc(mem_properties.memoryTypeCount * sizeof *pool.mem_type_alloc_sizes);
+ memset(pool.mem_type_alloc_sizes, 0, mem_properties.memoryTypeCount * sizeof *pool.mem_type_alloc_sizes);
+
+ pool.mem_pools = malloc(mem_properties.memoryTypeCount * sizeof *pool.mem_pools);
+ memset(pool.mem_pools, 0, mem_properties.memoryTypeCount * sizeof *pool.mem_pools);
+
+ pool.memory_type_count = mem_properties.memoryTypeCount;
+ pool.page_size = device_limits.bufferImageGranularity;
+ pool.block_min_size = pool.page_size * 10;
+
+ return pool;
+}
+
+static uint32_t
+rend_vk_pool_add_block(RendVkPoolAllocator *pool, VkDeviceSize size, uint32_t memory_type, VkMemoryPropertyFlags properties, bool fit_to_alloc)
+{
+ VkDeviceSize new_pool_size = size * 2;
+ new_pool_size = (new_pool_size < pool->block_min_size) ? pool->block_min_size : new_pool_size;
+
+ VkMemoryAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = new_pool_size,
+ .memoryTypeIndex = rend_vk_memory_find_index(pool->physical_device, memory_type, properties)
+ };
+
+ RendVKPoolBlock block = {0};
+ VkResult res = vkAllocateMemory(pool->logical_device, &alloc_info, pool->allocator, &block.address.handle);
+ block.address.type = memory_type;
+ block.address.size = new_pool_size;
+
+ RendVkPoolMemory *mem_pool = &pool->mem_pools[memory_type];
+ p_darray_push(mem_pool->block_darr, block);
+
+ RendVkPoolLayout layout = {
+ .offset = 0,
+ .size = new_pool_size
+ };
+ p_darray_push(mem_pool->block_darr[pool_size - 1].layout_darr, layout);
+
+ pool->total_allocations++;
+
+ size_t pool_size = p_darray_len(mem_pool->block_darr);
+ return pool_size - 1;
+}
+
+static RendVkMemory
+rend_vk_pool_alloc(RendVkPoolAllocator *pool, VkDeviceSize size, uint32_t usage, uint32_t memory_type, VkMemoryPropertyFlags properties)
+{
+ RendVkPoolMemory *mem_pool = &pool->mem_pools[memory_type];
+
+ VkDeviceSize requested_alloc_size = ((size / pool->page_size) + 1) * pool->page_size;
+ pool->mem_type_alloc_sizes[memory_type] += requested_alloc_size;
+
+ /* find free chunk for allocation
+ * TODO: free list?
+ */
+ int whole_page = usage != VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ uint64_t block_idx = UINT64_MAX;
+ uint64_t span_idx = UINT64_MAX;
+ for (uint32_t i = 0; i < p_darray_len(mem_pool->block_darr); ++i) {
+ for (uint32_t j = 0; j < p_darray_len(mem_pool->block_darr[i].layout_darr); ++j) {
+ int valid = (whole_page) ? mem_pool->block_darr[i].layout_darr[j].offset == 0 : 1;
+ if (mem_pool->block_darr[i].layout_darr[j].size >= size && valid) {
+ block_idx = i;
+ span_idx = j;
+ }
+ }
+ }
+
+ if (block_idx == UINT64_MAX || span_idx == UINT64_MAX) {
+ block_idx = rend_vk_pool_add_block(pool, size, memory_type, properites, whole_page);
+ span_idx = 0;
+ }
+
+ mem_pool->block_darr[block_idx].reserved = whole_page;
+
+ RendVkMemory memory = {
+ .device_memory = mem_pool->block_darr[block_idx].address.handle,
+ .size = size,
+ .offset = mem_pool->block_darr[block_idx].layout_darr[span_idx].offset,
+ .type = memory_type,
+
+ .logical_device = pool->logical_device,
+ .allocator = pool->allocator,
+ .host_mapped_memory = 0,
+ .physical_device = pool->physical_device,
+ .properties = properties,
+
+ .id = block_idx,
+ };
+
+ /* mark chunck */
+ mem_pool->block_darr[block_idx].layout_darr[span_idx].offset += size;
+ mem_pool->block_darr[block_idx].layout_darr[span_idx].size -= size;
+ return memory;
+}
+
+
+static void
+rend_vk_pool_free(RendVkPoolAllocator *pool, RendVkMemory *memory)
+{
+ VkDeviceSize requested = ((memory->size / pool->page_size) + 1) * pool->page_size;
+
+ RendVkPoolMemory *mem_pool = &pool->mem_pools[memory->type];
+
+ pool->blocks_[allocation.id].pageReserved = false;
+
+ mem_pool->block_darr[block_idx].layout_darr[span_idx].offset += size;
+ mem_pool->block_darr[block_idx].layout_darr[span_idx].size -= size;
+
+ OffsetSize span = {allocation.offset, requestedAllocSize };
+ bool found = false;
+
+ uint32_t numLayoutMems = pool.blocks[allocation.id].layout.size();
+ for (uint32_t j = 0; j < numLayoutMems; ++j)
+ {
+ if (pool.blocks[allocation.id].layout[j].offset == requestedAllocSize +allocation.offset)
+ {
+ pool.blocks[allocation.id].layout[j].offset = allocation.offset;
+ pool.blocks[allocation.id].layout[j].size += requestedAllocSize;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ state.memPools[allocation.type].blocks[allocation.id].layout.push_back(span);
+ state.memTypeAllocSizes[allocation.type] -= requestedAllocSize;
+ }
+}
+
+static void
+rend_vk_pool_destroy(RendVkPoolAllocator *pool)
+{
+ if (pool->mem_pools) {
+ for (size_t u = 0; u < pool->memory_type_count; ++u) {
+ RendVkPoolMemory mem_pool = pool->mem_pools[u];
+ for (size_t b = 0; b < p_darray_len(mem_pool.block_darr); ++b) {
+ RendVKPoolBlock block = mem_pool.block_darr[b];
+ p_darray_destroy(block.layout_darr);
+ }
+ p_darray_destroy(mem_pool.block_darr);
+ }
+ free(pool->mem_pools);
+ pool->mem_pools = 0;
+ }
+ if (pool->mem_type_alloc_sizes) {
+ free(pool->mem_type_alloc_sizes);
+ pool->mem_type_alloc_sizes = 0;
+ }
+ memset(pool, 0, sizeof *pool);
+}
+#endif
diff --git a/unused code/rend_vk_sbta.c b/unused code/rend_vk_sbta.c
new file mode 100644
index 0000000..7bc51c4
--- /dev/null
+++ b/unused code/rend_vk_sbta.c
@@ -0,0 +1,331 @@
+#pragma once
+#include "rend_internal.h"
+#include "rend_vk_internal.h"
+#include <vulkan/vulkan_core.h>
+
+/*
+ * Sparse Bindless Texture Array (SBTA)
+ *
+ */
+
+struct RendVkSbta {
+ VkDevice logical_device;
+ RendVkImage image; /* single 2D array image, arrayLayers = max_layers */
+ VkImageView *views; /* per-layer VkImageView array */
+ VkFormat format;
+ VkExtent2D extent;
+ uint32_t max_layers;
+ uint32_t mip_levels;
+ uint64_t *bitmap; /* 1 bit per layer slot */
+ uint32_t bitmap_word_count;
+ uint32_t allocated_count;
+ RendMemory memory;
+};
+
+static inline uint32_t
+rend_vk_sbta_mip_count(uint32_t w, uint32_t h)
+{
+ uint32_t v = (w > h) ? w : h;
+ uint32_t levels = 1;
+ while (v >>= 1) { levels++; }
+ return levels;
+}
+
+static void
+rend_vk_sbta_create(RendVkSbta *sbta, VkDevice logical_device, VkExtent2D extent, uint32_t layers)
+{
+ memset(sbta, 0, sizeof *sbta);
+ sbta->logical_device = logical_device;
+ sbta->format = VK_FORMAT_R8G8B8A8_SRGB;
+ sbta->extent = extent;
+ sbta->max_layers = layers;
+ sbta->mip_levels = rend_vk_sbta_mip_count(extent.width, extent.height);
+
+ /* create image */
+ sbta->image = rend_vk_image_create(
+ logical_device,
+ VK_IMAGE_TYPE_2D,
+ extent.width, extent.height,
+ sbta->format,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ 1, /* depth */
+ sbta->mip_levels,
+ layers,
+ VK_SAMPLE_COUNT_1_BIT,
+ VK_SHARING_MODE_EXCLUSIVE
+ );
+
+ /* allocate per-layer views array */
+ sbta->views = rmalloc(layers * sizeof *sbta->views);
+ memset(sbta->views, 0, layers * sizeof *sbta->views);
+
+ /* bitmap: ceil(layers / 64) words */
+ sbta->bitmap_word_count = (layers + 63) / 64;
+ sbta->bitmap = rmalloc(sbta->bitmap_word_count * sizeof *sbta->bitmap);
+ memset(sbta->bitmap, 0, sbta->bitmap_word_count * sizeof *sbta->bitmap);
+ sbta->allocated_count = 0;
+}
+
+static void
+rend_vk_sbta_create_views(RendVkSbta *sbta)
+{
+ for (uint32_t i = 0; i < sbta->max_layers; ++i) {
+ VkImageViewCreateInfo view_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = sbta->image.handle,
+ .format = sbta->format,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .subresourceRange.baseMipLevel = 0,
+ .subresourceRange.levelCount = sbta->mip_levels,
+ .subresourceRange.baseArrayLayer = i,
+ .subresourceRange.layerCount = 1,
+ };
+ vkCreateImageView(sbta->logical_device, &view_info, vk_allocator, &sbta->views[i]);
+ }
+}
+
+static void
+rend_vk_sbta_destroy(RendVkSbta *sbta)
+{
+ for (uint32_t i = 0; i < sbta->max_layers; ++i) {
+ if (sbta->views[i]) {
+ vkDestroyImageView(sbta->logical_device, sbta->views[i], vk_allocator);
+ }
+ }
+
+ rend_vk_image_destroy(&sbta->image);
+
+ rfree(sbta->views);
+ rfree(sbta->bitmap);
+ memset(sbta, 0, sizeof *sbta);
+}
+
+static uint64_t
+rend_vk_sbta_alloc(RendVkSbta *sbta)
+{
+ for (uint32_t w = 0; w < sbta->bitmap_word_count; ++w) {
+ if (sbta->bitmap[w] == ~(uint64_t)0) continue; /* word full */
+
+ /* find first zero bit */
+ uint64_t word = sbta->bitmap[w];
+ uint64_t bit = ~word & (word + 1); /* isolate lowest zero bit */
+ uint32_t bit_index = 0;
+ uint64_t tmp = bit;
+ while (tmp >>= 1) { bit_index++; }
+
+ uint64_t slot = (uint64_t)w * 64 + bit_index;
+ if (slot >= sbta->max_layers) return UINT64_MAX; /* past capacity */
+
+ sbta->bitmap[w] |= bit;
+ sbta->allocated_count++;
+ return slot;
+ }
+ return UINT64_MAX; /* full */
+}
+
+static void
+rend_vk_sbta_free(RendVkSbta *sbta, uint64_t idx)
+{
+ assert(idx < sbta->max_layers && "SBTA free: index out of range");
+
+ uint32_t word = (uint32_t)(idx / 64);
+ uint32_t bit = (uint32_t)(idx % 64);
+ assert((sbta->bitmap[word] & (1ULL << bit)) && "SBTA free: slot not allocated (double free?)");
+
+ sbta->bitmap[word] &= ~(1ULL << bit);
+ sbta->allocated_count--;
+}
+
+static void
+rend_vk_sbta_upload(RendVkSbta *sbta, VkCommandBuffer cmd, RendVkArenaAllocator *staging_arena, uint64_t slot, void *pixels, uint64_t size)
+{
+ assert(slot < sbta->max_layers);
+ assert(pixels && size > 0);
+
+ /* ---- staging buffer ---- */
+ VkBufferCreateInfo buf_info = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .size = size,
+ .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ };
+
+ VkBuffer staging_buf;
+ vkCreateBuffer(sbta->logical_device, &buf_info, vk_allocator, &staging_buf);
+
+ VkMemoryRequirements staging_reqs;
+ vkGetBufferMemoryRequirements(sbta->logical_device, staging_buf, &staging_reqs);
+
+ uint32_t host_index = vk_device.host_index;
+ RendMemory staging_mem = rend_vk_arena_alloc(staging_arena, staging_reqs.size, host_index);
+ vkBindBufferMemory(sbta->logical_device, staging_buf, (VkDeviceMemory) staging_mem.device_memory, staging_mem.offset);
+
+ /* copy pixels into staging */
+ memcpy(staging_mem.host_mapped_memory, pixels, size);
+
+ /* ---- transition layer mip 0: UNDEFINED -> TRANSFER_DST ---- */
+ VkImageMemoryBarrier2 barrier_to_dst = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .srcStageMask = VK_PIPELINE_STAGE_2_NONE,
+ .srcAccessMask = VK_ACCESS_2_NONE,
+ .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .image = sbta->image.handle,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = sbta->mip_levels,
+ .baseArrayLayer = (uint32_t)slot,
+ .layerCount = 1,
+ },
+ };
+
+ VkDependencyInfo dep_to_dst = {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .imageMemoryBarrierCount = 1,
+ .pImageMemoryBarriers = &barrier_to_dst,
+ };
+ vkCmdPipelineBarrier2(cmd, &dep_to_dst);
+
+ /* ---- copy staging -> image mip 0 ---- */
+ VkBufferImageCopy copy_region = {
+ .bufferOffset = 0,
+ .imageSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = (uint32_t)slot,
+ .layerCount = 1,
+ },
+ .imageExtent = { sbta->extent.width, sbta->extent.height, 1 },
+ };
+
+ vkCmdCopyBufferToImage(cmd, staging_buf, sbta->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
+
+ /* ---- generate mipmaps via blit chain ---- */
+ int32_t mip_w = (int32_t)sbta->extent.width;
+ int32_t mip_h = (int32_t)sbta->extent.height;
+
+ for (uint32_t mip = 1; mip < sbta->mip_levels; ++mip) {
+ /* transition previous mip: TRANSFER_DST -> TRANSFER_SRC */
+ VkImageMemoryBarrier2 barrier_src = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .image = sbta->image.handle,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = mip - 1,
+ .levelCount = 1,
+ .baseArrayLayer = (uint32_t)slot,
+ .layerCount = 1,
+ },
+ };
+
+ VkDependencyInfo dep_src = {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .imageMemoryBarrierCount = 1,
+ .pImageMemoryBarriers = &barrier_src,
+ };
+ vkCmdPipelineBarrier2(cmd, &dep_src);
+
+ /* blit from mip-1 to mip */
+ int32_t next_w = (mip_w > 1) ? mip_w / 2 : 1;
+ int32_t next_h = (mip_h > 1) ? mip_h / 2 : 1;
+
+ VkImageBlit2 blit = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
+ .srcSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = mip - 1,
+ .baseArrayLayer = (uint32_t)slot,
+ .layerCount = 1,
+ },
+ .srcOffsets = { {0, 0, 0}, {mip_w, mip_h, 1} },
+ .dstSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = mip,
+ .baseArrayLayer = (uint32_t)slot,
+ .layerCount = 1,
+ },
+ .dstOffsets = { {0, 0, 0}, {next_w, next_h, 1} },
+ };
+
+ VkBlitImageInfo2 blit_info = {
+ .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
+ .srcImage = sbta->image.handle,
+ .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .dstImage = sbta->image.handle,
+ .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .regionCount = 1,
+ .pRegions = &blit,
+ .filter = VK_FILTER_LINEAR,
+ };
+ vkCmdBlitImage2(cmd, &blit_info);
+
+ mip_w = next_w;
+ mip_h = next_h;
+ }
+
+ /* ---- final transition: all mips -> SHADER_READ_ONLY ---- */
+ /* last mip is still TRANSFER_DST, all others are TRANSFER_SRC */
+
+ /* transition last mip: TRANSFER_DST -> SHADER_READ_ONLY */
+ VkImageMemoryBarrier2 barriers_final[2] = {
+ /* mips 0..N-2: TRANSFER_SRC -> SHADER_READ_ONLY */
+ {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
+ .dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .image = sbta->image.handle,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = (sbta->mip_levels > 1) ? sbta->mip_levels - 1 : 1,
+ .baseArrayLayer = (uint32_t)slot,
+ .layerCount = 1,
+ },
+ },
+ /* last mip: TRANSFER_DST -> SHADER_READ_ONLY */
+ {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
+ .dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .image = sbta->image.handle,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = sbta->mip_levels - 1,
+ .levelCount = 1,
+ .baseArrayLayer = (uint32_t)slot,
+ .layerCount = 1,
+ },
+ },
+ };
+
+ uint32_t barrier_count = (sbta->mip_levels > 1) ? 2 : 1;
+
+ /* if only 1 mip, use second barrier (TRANSFER_DST path) */
+ VkImageMemoryBarrier2 *barrier_ptr = (sbta->mip_levels > 1) ? barriers_final : &barriers_final[1];
+
+ VkDependencyInfo dep_final = {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .imageMemoryBarrierCount = barrier_count,
+ .pImageMemoryBarriers = barrier_ptr,
+ };
+ vkCmdPipelineBarrier2(cmd, &dep_final);
+}