vk: fix texture corruption after a few changelevels

We were running out of descriptor sets, as they weren't being freed at all.
Now there's a 1-1 table of texture-descset, and no way to run out of them.

Better solution would be to move this descset management to vk_render, and allocate them dynamically from a smaller pool.
This commit is contained in:
Ivan 'provod' Avdeev 2023-10-17 13:17:47 -04:00
parent e1d478fa28
commit fd97dc2c24
5 changed files with 75 additions and 105 deletions

View File

@ -2,7 +2,7 @@
#include "eiface.h" // ARRAYSIZE
descriptor_pool_t vk_desc;
descriptor_pool_t vk_desc_fixme;
qboolean VK_DescriptorInit( void )
{
@ -14,7 +14,7 @@ qboolean VK_DescriptorInit( void )
.descriptorCount = MAX_TEXTURES,
}, {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
.descriptorCount = ARRAYSIZE(vk_desc.ubo_sets),
.descriptorCount = ARRAYSIZE(vk_desc_fixme.ubo_sets),
/*
}, {
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
@ -33,7 +33,7 @@ qboolean VK_DescriptorInit( void )
dpci.maxSets = max_desc_sets;
XVK_CHECK(vkCreateDescriptorPool(vk_core.device, &dpci, NULL, &vk_desc.pool));
XVK_CHECK(vkCreateDescriptorPool(vk_core.device, &dpci, NULL, &vk_desc_fixme.pool));
{
const int num_sets = MAX_TEXTURES;
@ -53,21 +53,21 @@ qboolean VK_DescriptorInit( void )
VkDescriptorSetLayout* tmp_layouts = Mem_Malloc(vk_core.pool, sizeof(VkDescriptorSetLayout) * num_sets);
const VkDescriptorSetAllocateInfo dsai = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = vk_desc.pool,
.descriptorPool = vk_desc_fixme.pool,
.descriptorSetCount = num_sets,
.pSetLayouts = tmp_layouts,
};
XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc.one_texture_layout));
XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc_fixme.one_texture_layout));
for (int i = 0; i < num_sets; ++i)
tmp_layouts[i] = vk_desc.one_texture_layout;
tmp_layouts[i] = vk_desc_fixme.one_texture_layout;
XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc.sets));
XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc_fixme.texture_sets));
Mem_Free(tmp_layouts);
}
{
const int num_sets = ARRAYSIZE(vk_desc.ubo_sets);
const int num_sets = ARRAYSIZE(vk_desc_fixme.ubo_sets);
// ... TODO find better place for this; this should be per-pipeline/shader
VkDescriptorSetLayoutBinding bindings[] = { {
.binding = 0,
@ -84,15 +84,15 @@ qboolean VK_DescriptorInit( void )
VkDescriptorSetLayout* tmp_layouts = Mem_Malloc(vk_core.pool, sizeof(VkDescriptorSetLayout) * num_sets);
VkDescriptorSetAllocateInfo dsai = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = vk_desc.pool,
.descriptorPool = vk_desc_fixme.pool,
.descriptorSetCount = num_sets,
.pSetLayouts = tmp_layouts,
};
XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc.one_uniform_buffer_layout));
XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc_fixme.one_uniform_buffer_layout));
for (int i = 0; i < num_sets; ++i)
tmp_layouts[i] = vk_desc.one_uniform_buffer_layout;
tmp_layouts[i] = vk_desc_fixme.one_uniform_buffer_layout;
XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc.ubo_sets));
XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc_fixme.ubo_sets));
Mem_Free(tmp_layouts);
}
@ -102,9 +102,9 @@ qboolean VK_DescriptorInit( void )
void VK_DescriptorShutdown( void )
{
vkDestroyDescriptorPool(vk_core.device, vk_desc.pool, NULL);
vkDestroyDescriptorSetLayout(vk_core.device, vk_desc.one_texture_layout, NULL);
vkDestroyDescriptorSetLayout(vk_core.device, vk_desc.one_uniform_buffer_layout, NULL);
vkDestroyDescriptorPool(vk_core.device, vk_desc_fixme.pool, NULL);
vkDestroyDescriptorSetLayout(vk_core.device, vk_desc_fixme.one_texture_layout, NULL);
vkDestroyDescriptorSetLayout(vk_core.device, vk_desc_fixme.one_uniform_buffer_layout, NULL);
}
void VK_DescriptorsCreate(vk_descriptors_t *desc)

View File

@ -4,16 +4,12 @@
#include "vk_const.h"
// Only used for traditional renderer
typedef struct descriptor_pool_s
{
VkDescriptorPool pool;
// TODO don't expose this, make a function to alloc desc set with given layout instead
int next_free;
//uint32_t *free_set;
// * 2 because of unorm views for trad renderer
VkDescriptorSet sets[MAX_TEXTURES * 2];
VkDescriptorSet texture_sets[MAX_TEXTURES];
VkDescriptorSetLayout one_texture_layout;
// FIXME HOW THE F
@ -21,7 +17,8 @@ typedef struct descriptor_pool_s
VkDescriptorSetLayout one_uniform_buffer_layout;
} descriptor_pool_t;
extern descriptor_pool_t vk_desc;
// FIXME: move to traditional renderer
extern descriptor_pool_t vk_desc_fixme;
qboolean VK_DescriptorInit( void );
void VK_DescriptorShutdown( void );

View File

@ -145,7 +145,7 @@ static qboolean createPipelines( void )
/* }; */
VkDescriptorSetLayout descriptor_layouts[] = {
vk_desc.one_texture_layout,
vk_desc_fixme.one_texture_layout,
};
VkPipelineLayoutCreateInfo plci = {

View File

@ -62,10 +62,10 @@ static qboolean createPipelines( void )
/* }; */
VkDescriptorSetLayout descriptor_layouts[] = {
vk_desc.one_uniform_buffer_layout,
vk_desc.one_texture_layout,
vk_desc.one_texture_layout,
vk_desc.one_uniform_buffer_layout,
vk_desc_fixme.one_uniform_buffer_layout,
vk_desc_fixme.one_texture_layout,
vk_desc_fixme.one_texture_layout,
vk_desc_fixme.one_uniform_buffer_layout,
};
VkPipelineLayoutCreateInfo plci = {
@ -321,7 +321,7 @@ qboolean VK_RenderInit( void ) {
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
.pBufferInfo = &dbi_uniform_data,
.dstSet = vk_desc.ubo_sets[0], // FIXME
.dstSet = vk_desc_fixme.ubo_sets[0], // FIXME
}, {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstBinding = 0,
@ -329,7 +329,7 @@ qboolean VK_RenderInit( void ) {
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
.pBufferInfo = &dbi_uniform_lights,
.dstSet = vk_desc.ubo_sets[1], // FIXME
.dstSet = vk_desc_fixme.ubo_sets[1], // FIXME
}};
vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL);
}
@ -552,7 +552,7 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw )
vkCmdBindIndexBuffer(cmdbuf, geom_buffer, 0, VK_INDEX_TYPE_UINT16);
}
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 3, 1, vk_desc.ubo_sets + 1, 1, &dlights_ubo_offset);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 3, 1, vk_desc_fixme.ubo_sets + 1, 1, &dlights_ubo_offset);
for (int i = 0; i < g_render_state.num_draw_commands; ++i) {
const draw_command_t *const draw = g_render_state.draw_commands + i;
@ -579,7 +579,7 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw )
if (ubo_offset != draw->draw.ubo_offset)
{
ubo_offset = draw->draw.ubo_offset;
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 0, 1, vk_desc.ubo_sets, 1, &ubo_offset);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 0, 1, vk_desc_fixme.ubo_sets, 1, &ubo_offset);
}
if (pipeline != draw->draw.pipeline_index) {

View File

@ -697,59 +697,52 @@ static VkSampler pickSamplerForFlags( texFlags_t flags ) {
return tglob.default_sampler_fixme;
}
static void prepDestriptorSets(vk_texture_t* const tex, colorspace_hint_e colorspace_hint) {
static void setDescriptorSet(vk_texture_t* const tex, colorspace_hint_e colorspace_hint) {
// FIXME detect skybox some other way
if (tex->vk.image.layers > 1)
return;
const int index = tex - vk_textures;
ASSERT(index >= 0);
ASSERT(index < MAX_TEXTURES);
const VkImageView view = tex->vk.image.view != VK_NULL_HANDLE ? tex->vk.image.view : vk_textures[tglob.defaultTexture].vk.image.view;
if (view == VK_NULL_HANDLE)
return;
VkDescriptorImageInfo dii = {
.imageView = view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.sampler = pickSamplerForFlags( tex->flags ),
};
// Set descriptor for bindless/ray tracing
tglob.dii_all_textures[index] = dii;
// Continue with setting unorm descriptor for traditional renderer
// TODO how should we approach this:
// - per-texture desc sets can be inconvenient if texture is used in different incompatible contexts
// - update descriptor sets in batch?
if (vk_desc.next_free < MAX_TEXTURES-2) {
const int index = tex - vk_textures;
const VkDescriptorSet ds = vk_desc.sets[vk_desc.next_free++];
const VkDescriptorSet ds_unorm =
(colorspace_hint == kColorspaceGamma && tex->vk.image.view_unorm != VK_NULL_HANDLE)
? vk_desc.sets[vk_desc.next_free++] : VK_NULL_HANDLE;
const VkDescriptorImageInfo dii = {
.imageView = tex->vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.sampler = pickSamplerForFlags( tex->flags ),
};
if (colorspace_hint == kColorspaceGamma && tex->vk.image.view_unorm != VK_NULL_HANDLE)
dii.imageView = tex->vk.image.view_unorm;
const VkDescriptorImageInfo dii_unorm = {
.imageView = tex->vk.image.view_unorm,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.sampler = dii.sampler,
};
const VkDescriptorSet ds = vk_desc_fixme.texture_sets[index];
VkWriteDescriptorSet wds[1] = { {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &dii,
.dstSet = ds,
}};
VkWriteDescriptorSet wds[2] = { {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &dii,
.dstSet = ds,
}, {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &dii_unorm,
.dstSet = ds_unorm,
}};
vkUpdateDescriptorSets(vk_core.device, ds_unorm != VK_NULL_HANDLE ? 2 : 1 , wds, 0, NULL);
vkUpdateDescriptorSets(vk_core.device, COUNTOF(wds), wds, 0, NULL);
// FIXME detect skybox some other way
if (tex->vk.image.layers == 1) {
tglob.dii_all_textures[index] = dii;
}
tex->vk.descriptor_unorm = ds_unorm != VK_NULL_HANDLE ? ds_unorm : ds;
}
else
{
tex->vk.descriptor_unorm = VK_NULL_HANDLE;
}
tex->vk.descriptor_unorm = ds;
}
static qboolean uploadRawKtx2( vk_texture_t *tex, const rgbdata_t* pic ) {
@ -835,34 +828,17 @@ static qboolean uploadRawKtx2( vk_texture_t *tex, const rgbdata_t* pic ) {
R_VkImageUploadEnd(&tex->vk.image);
}
// KTX2 textures are inaccessible from trad renderer (for now)
tex->vk.descriptor_unorm = VK_NULL_HANDLE;
{
// KTX2 textures are inaccessible from trad renderer (for now)
tex->vk.descriptor_unorm = VK_NULL_HANDLE;
// TODO how should we approach this:
// - per-texture desc sets can be inconvenient if texture is used in different incompatible contexts
// - update descriptor sets in batch?
if (vk_desc.next_free != MAX_TEXTURES) {
const int num_layers = 1; // TODO cubemap
const int index = tex - vk_textures;
VkDescriptorImageInfo dii_tmp;
// FIXME handle cubemaps properly w/o this garbage. they should be the same as regular textures.
VkDescriptorImageInfo *const dii_tex = (num_layers == 1) ? tglob.dii_all_textures + index : &dii_tmp;
*dii_tex = (VkDescriptorImageInfo){
const VkDescriptorImageInfo dii = {
.imageView = tex->vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.sampler = pickSamplerForFlags( tex->flags ),
};
const VkWriteDescriptorSet wds[] = { {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = dii_tex,
.dstSet = vk_desc.sets[vk_desc.next_free++],
}};
vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL);
tglob.dii_all_textures[index] = dii;
}
g_textures.stats.size_total += tex->total_size;
@ -989,7 +965,7 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers,
}
}
prepDestriptorSets(tex, colorspace_hint);
setDescriptorSet(tex, colorspace_hint);
g_textures.stats.size_total += tex->total_size;
g_textures.stats.count++;
@ -1143,11 +1119,8 @@ void R_FreeTexture( unsigned int texnum ) {
g_textures.stats.count--;
memset(tex, 0, sizeof(*tex));
tglob.dii_all_textures[texnum] = (VkDescriptorImageInfo){
.imageView = vk_textures[tglob.defaultTexture].vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.sampler = tglob.default_sampler_fixme,
};
// Reset descriptor sets to default texture
setDescriptorSet(tex, kColorspaceNative);
end:
APROF_SCOPE_END(free);