diff --git a/ref_vk/vk_buffer.c b/ref_vk/vk_buffer.c index a3326d20..b85ba75f 100644 --- a/ref_vk/vk_buffer.c +++ b/ref_vk/vk_buffer.c @@ -1,22 +1,5 @@ #include "vk_buffer.h" -#include - -vk_global_buffer_t g_vk_buffers = {0}; - -#define DEFAULT_STAGING_SIZE (16*1024*1024) - -qboolean VK_BuffersInit( void ) { - if (!VK_BufferCreate("staging", &g_vk_buffers.staging, DEFAULT_STAGING_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) - return false; - - return true; -} - -void VK_BuffersDestroy( void ) { - VK_BufferDestroy(&g_vk_buffers.staging); -} - qboolean VK_BufferCreate(const char *debug_name, vk_buffer_t *buf, uint32_t size, VkBufferUsageFlags usage, VkMemoryPropertyFlags flags) { VkBufferCreateInfo bci = { @@ -50,7 +33,6 @@ void VK_BufferDestroy(vk_buffer_t *buf) { buf->buffer = VK_NULL_HANDLE; } - // FIXME when there are many allocation per VkDeviceMemory, fix this if (buf->devmem.device_memory) { VK_DevMemFree(&buf->devmem); buf->devmem.device_memory = VK_NULL_HANDLE; diff --git a/ref_vk/vk_buffer.h b/ref_vk/vk_buffer.h index 2b1538cb..15de1198 100644 --- a/ref_vk/vk_buffer.h +++ b/ref_vk/vk_buffer.h @@ -11,15 +11,6 @@ typedef struct vk_buffer_s { uint32_t size; } vk_buffer_t; -typedef struct { - vk_buffer_t staging; -} vk_global_buffer_t; - -extern vk_global_buffer_t g_vk_buffers; - -qboolean VK_BuffersInit( void ); -void VK_BuffersDestroy( void ); - qboolean VK_BufferCreate(const char *debug_name, vk_buffer_t *buf, uint32_t size, VkBufferUsageFlags usage, VkMemoryPropertyFlags flags); void VK_BufferDestroy(vk_buffer_t *buf); diff --git a/ref_vk/vk_core.c b/ref_vk/vk_core.c index 5787f925..67070665 100644 --- a/ref_vk/vk_core.c +++ b/ref_vk/vk_core.c @@ -4,7 +4,7 @@ #include "vk_textures.h" #include "vk_2d.h" #include "vk_renderstate.h" -#include "vk_buffer.h" +#include "vk_staging.h" #include "vk_framectl.h" #include "vk_brush.h" #include "vk_scene.h" @@ -703,7 +703,7 @@ qboolean R_VkInit( void ) if (!VK_DevMemInit()) return false; - if (!VK_BuffersInit()) + if (!R_VkStagingInit()) return false; // TODO move this to vk_texture module @@ -789,7 +789,7 @@ void R_VkShutdown( void ) { VK_DescriptorShutdown(); vkDestroySampler(vk_core.device, vk_core.default_sampler, NULL); - VK_BuffersDestroy(); + R_VkStagingShutdown(); VK_DevMemDestroy(); diff --git a/ref_vk/vk_staging.c b/ref_vk/vk_staging.c new file mode 100644 index 00000000..6f837b89 --- /dev/null +++ b/ref_vk/vk_staging.c @@ -0,0 +1,143 @@ +#include "vk_staging.h" +#include "vk_buffer.h" + +#include + +#define DEFAULT_STAGING_SIZE (16*1024*1024) +#define MAX_STAGING_ALLOCS (1024) + +typedef struct { + int offset, size; + enum { DestNone, DestBuffer, DestImage } dest_type; + union { + struct { + VkBuffer buffer; + VkDeviceSize offset; + } buffer; + struct { + VkImage image; + VkImageLayout layout; + VkBufferImageCopy region; + } image; + }; +} staging_alloc_t; + +static struct { + vk_buffer_t buffer; + staging_alloc_t allocs[MAX_STAGING_ALLOCS]; + int num_allocs; +} g_staging = {0}; + +qboolean R_VkStagingInit(void) { + if (!VK_BufferCreate("staging", &g_staging.buffer, DEFAULT_STAGING_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + return false; + + return true; +} + +void R_VkStagingShutdown(void) { + VK_BufferDestroy(&g_staging.buffer); +} + +vk_staging_region_t R_VkStagingLock(size_t size) { + const int offset = g_staging.num_allocs > 0 ? g_staging.allocs[g_staging.num_allocs - 1].offset + g_staging.allocs[g_staging.num_allocs - 1].size : 0; + + if ( g_staging.num_allocs >= MAX_STAGING_ALLOCS ) + return (vk_staging_region_t){0}; + + if ( offset + size > g_staging.buffer.size ) + return (vk_staging_region_t){0}; + + memset(g_staging.allocs + g_staging.num_allocs, 0, sizeof(staging_alloc_t)); + g_staging.allocs[g_staging.num_allocs].offset = offset; + g_staging.allocs[g_staging.num_allocs].size = size; + g_staging.num_allocs++; + return (vk_staging_region_t){(char*)g_staging.buffer.mapped + offset, size, g_staging.num_allocs - 1}; +} + +/* +void R_VkStagingUnlockToBuffer(const vk_staging_region_t* region, VkBuffer dest, size_t dest_offset) { + ASSERT(region->internal_id_ >= 0 && region->internal_id_ < g_staging.num_allocs); + ASSERT(g_staging.allocs[region->internal_id_].dest == VK_NULL_HANDLE); + + g_staging.allocs[region->internal_id_].dest = dest; + g_staging.allocs[region->internal_id_].dest_offset = dest_offset; +} +*/ + +void R_VkStagingUnlockToImage(const vk_staging_region_t* region, VkBufferImageCopy* dest_region, VkImageLayout layout, VkImage dest) { + staging_alloc_t *alloc; + ASSERT(region->internal_id_ >= 0 && region->internal_id_ < g_staging.num_allocs); + ASSERT(g_staging.allocs[region->internal_id_].dest_type == DestNone); + + alloc = g_staging.allocs + region->internal_id_; + alloc->dest_type = DestImage; + alloc->image.layout = layout; + alloc->image.image = dest; + alloc->image.region = *dest_region; + alloc->image.region.bufferOffset += alloc->offset; +} + +static void copyImage(VkCommandBuffer cmdbuf, const staging_alloc_t *alloc) { + vkCmdCopyBufferToImage(cmdbuf, g_staging.buffer.buffer, alloc->image.image, alloc->image.layout, 1, &alloc->image.region); +} + +void R_VkStagingCommit(VkCommandBuffer cmdbuf) { + for ( int i = 0; i < g_staging.num_allocs; i++ ) { + staging_alloc_t *const alloc = g_staging.allocs + i; + ASSERT(alloc->dest_type != DestNone); + switch (alloc->dest_type) { + case DestImage: + copyImage(cmdbuf, alloc); + break; + case DestBuffer: + ASSERT(!"staging dest buffer is not implemented"); + break; + } + + alloc->dest_type = DestNone; + +#if 0 + // TODO coalesce staging regions for the same dest buffer + + const VkBufferCopy copy = { + .srcOffset = g_staging.allocs[i].offset, + .dstOffset = g_staging.allocs[i].dest_offset, + .size = g_staging.allocs[i].size + }; + vkCmdCopyBuffer(cmdbuf, g_staging.buffer.buffer, g_staging.allocs[i].dest, 1, ©); + + // TODO decide whether this needs to do anything with barriers + // here we can only collect dirty regions for each dest buffer +#endif + } + + g_staging.num_allocs = 0; +} + +void R_VkStagingFlushSync(void) { + if ( !g_staging.num_allocs ) + return; + + { + // FIXME get the right one + const VkCommandBuffer cmdbuf = vk_core.upload_pool.buffers[0]; + + const VkCommandBufferBeginInfo beginfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + + const VkSubmitInfo subinfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &cmdbuf, + }; + + XVK_CHECK(vkBeginCommandBuffer(cmdbuf, &beginfo)); + R_VkStagingCommit(cmdbuf); + XVK_CHECK(vkEndCommandBuffer(cmdbuf)); + XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, VK_NULL_HANDLE)); + XVK_CHECK(vkQueueWaitIdle(vk_core.queue)); + } +} diff --git a/ref_vk/vk_staging.h b/ref_vk/vk_staging.h new file mode 100644 index 00000000..ccd27b40 --- /dev/null +++ b/ref_vk/vk_staging.h @@ -0,0 +1,27 @@ +#pragma once + +#include "vk_core.h" + +qboolean R_VkStagingInit(void); +void R_VkStagingShutdown(void); + +//void *R_VkStagingAlloc(size_t size, VkBuffer dest, size_t dest_offset); + +typedef struct { + void *ptr; + size_t size; + int internal_id_; +} vk_staging_region_t; +vk_staging_region_t R_VkStagingLock(size_t size); +void R_VkStagingUnlockToBuffer(const vk_staging_region_t* region, VkBuffer dest, size_t dest_offset); +void R_VkStagingUnlockToImage(const vk_staging_region_t* region, VkBufferImageCopy* dest_region, VkImageLayout layout, VkImage dest); + +void R_VkStagingCommit(VkCommandBuffer cmdbuf); + +// Force commit synchronously +void R_VkStagingFlushSync(void); + +// TODO +// - [x] call init/shutdown from vk_core.ckkjkj +// - [x] use this in vk_texture.c +// - [ ] use this in vk_render.c diff --git a/ref_vk/vk_textures.c b/ref_vk/vk_textures.c index 76bc073c..042260af 100644 --- a/ref_vk/vk_textures.c +++ b/ref_vk/vk_textures.c @@ -2,7 +2,7 @@ #include "vk_common.h" #include "vk_core.h" -#include "vk_buffer.h" +#include "vk_staging.h" #include "vk_const.h" #include "vk_descriptor.h" #include "vk_mapents.h" // wadlist @@ -546,8 +546,6 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, } { - size_t staging_offset = 0; // TODO multiple staging buffer users params.staging->ptr - // 5. Create/get cmdbuf for transitions VkCommandBufferBeginInfo beginfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -587,7 +585,7 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, const size_t mip_size = CalcImageSize( pic->type, width, height, 1 ); VkBufferImageCopy region = {0}; - region.bufferOffset = staging_offset; + region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource = (VkImageSubresourceLayers){ @@ -602,20 +600,22 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, .depth = 1, }; - memcpy(((uint8_t*)g_vk_buffers.staging.mapped) + staging_offset, buf, mip_size); + vk_staging_region_t staging = R_VkStagingLock(mip_size); + ASSERT(staging.ptr); + memcpy(staging.ptr, buf, mip_size); + // Build mip in place for the next mip level if ( mip < mipCount - 1 ) { BuildMipMap( buf, width, height, 1, tex->flags ); } - // TODO we could do this only once w/ region array - vkCmdCopyBufferToImage(cmdbuf, g_vk_buffers.staging.buffer, tex->vk.image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - staging_offset += mip_size; + R_VkStagingUnlockToImage(&staging, ®ion, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, tex->vk.image.image); } } + R_VkStagingCommit(cmdbuf); + // 5.2 image:layout:DST -> image:layout:SAMPLED // 5.2.1 transitionToLayout(DST -> SHADER_READ_ONLY) image_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;