vk: draft staging api, use it for textures

This commit is contained in:
Ivan 'provod' Avdeev 2022-04-28 00:16:25 -07:00
parent 573d006c4a
commit a3ff75b48f
6 changed files with 182 additions and 39 deletions

View File

@ -1,22 +1,5 @@
#include "vk_buffer.h"
#include <memory.h>
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;

View File

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

View File

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

143
ref_vk/vk_staging.c Normal file
View File

@ -0,0 +1,143 @@
#include "vk_staging.h"
#include "vk_buffer.h"
#include <memory.h>
#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, &copy);
// 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));
}
}

27
ref_vk/vk_staging.h Normal file
View File

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

View File

@ -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, &region);
staging_offset += mip_size;
R_VkStagingUnlockToImage(&staging, &region, 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;