From 95e3e76fd9c789cbf2d4409ef49456addc71643f Mon Sep 17 00:00:00 2001 From: Vasco Date: Wed, 12 Aug 2026 17:47:50 +0100 Subject: rend 1.0.1 --- unused code/rend_vk_sbta.c | 331 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 unused code/rend_vk_sbta.c (limited to 'unused code/rend_vk_sbta.c') 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 + +/* + * 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, ©_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); +} -- cgit v1.3.1