From 01de5957d876f4668b32c2e241e84405666bf1e0 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Wed, 8 Mar 2023 14:16:21 -0800 Subject: [PATCH] vk: respect TF_NEAREST/CLAMP/BORDER flags (#471) Use these flags to pick the right sampler. Fixes issues with blurry and leaking fonts, lines in main menu tiles, etc. fixes #439, fixes #79 --- ref/vk/vk_core.c | 50 ++++++++++------- ref/vk/vk_core.h | 2 - ref/vk/vk_descriptor.c | 8 +-- ref/vk/vk_rtx.c | 3 +- ref/vk/vk_textures.c | 125 ++++++++++++++++++++++++++--------------- ref/vk/vk_textures.h | 10 ++++ 6 files changed, 126 insertions(+), 72 deletions(-) diff --git a/ref/vk/vk_core.c b/ref/vk/vk_core.c index 60e3991a..d29fd457 100644 --- a/ref/vk/vk_core.c +++ b/ref/vk/vk_core.c @@ -649,6 +649,35 @@ static qboolean initSurface( void ) return true; } +// TODO modules +/* +typedef struct r_vk_module_s { + qboolean (*init)(void); + void (*destroy)(void); + + // TODO next: dependecies, refcounts, ... +} r_vk_module_t; + +#define LIST_MODULES(X) ... + +=> +extern const r_vk_module_t vk_instance_module; +... +extern const r_vk_module_t vk_rtx_module; +... + +=> +static const r_vk_module_t *const modules[] = { + &vk_instance_module, + &vk_device_module, + &vk_aftermath_module, + &vk_texture_module, + ... + &vk_rtx_module, + ... +}; +*/ + qboolean R_VkInit( void ) { // FIXME !!!! handle initialization errors properly: destroy what has already been created @@ -716,26 +745,6 @@ qboolean R_VkInit( void ) if (!R_VkStagingInit()) return false; - // TODO move this to vk_texture module - { - VkSamplerCreateInfo sci = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,// TODO CLAMP_TO_EDGE, for menus - .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,//CLAMP_TO_EDGE, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .anisotropyEnable = vk_core.physical_device.anisotropy_enabled, - .maxAnisotropy = vk_core.physical_device.properties.limits.maxSamplerAnisotropy, - .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .minLod = 0.f, - .maxLod = 16., - }; - XVK_CHECK(vkCreateSampler(vk_core.device, &sci, NULL, &vk_core.default_sampler)); - } - if (!VK_PipelineInit()) return false; @@ -802,7 +811,6 @@ void R_VkShutdown( void ) { VK_DescriptorShutdown(); - vkDestroySampler(vk_core.device, vk_core.default_sampler, NULL); R_VkStagingShutdown(); VK_DevMemDestroy(); diff --git a/ref/vk/vk_core.h b/ref/vk/vk_core.h index cef65e4e..2ee3998a 100644 --- a/ref/vk/vk_core.h +++ b/ref/vk/vk_core.h @@ -54,8 +54,6 @@ typedef struct vulkan_core_s { VkDevice device; VkQueue queue; - VkSampler default_sampler; - unsigned int num_devices; ref_device_t *devices; } vulkan_core_t; diff --git a/ref/vk/vk_descriptor.c b/ref/vk/vk_descriptor.c index 581d43fe..0d0b2b95 100644 --- a/ref/vk/vk_descriptor.c +++ b/ref/vk/vk_descriptor.c @@ -38,20 +38,20 @@ qboolean VK_DescriptorInit( void ) { const int num_sets = MAX_TEXTURES; // ... TODO find better place for this; this should be per-pipeline/shader - VkDescriptorSetLayoutBinding bindings[] = { { + const VkDescriptorSetLayoutBinding bindings[] = { { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = &vk_core.default_sampler, + .pImmutableSamplers = NULL, }}; - VkDescriptorSetLayoutCreateInfo dslci = { + const VkDescriptorSetLayoutCreateInfo dslci = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = ARRAYSIZE(bindings), .pBindings = bindings, }; VkDescriptorSetLayout* tmp_layouts = Mem_Malloc(vk_core.pool, sizeof(VkDescriptorSetLayout) * num_sets); - VkDescriptorSetAllocateInfo dsai = { + const VkDescriptorSetAllocateInfo dsai = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorPool = vk_desc.pool, .descriptorSetCount = num_sets, diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index 83bcf416..cb14dd3c 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -117,9 +117,10 @@ void VK_RayNewMap( void ) { g_rtx.res[ExternalResource_skybox].resource = (vk_resource_t){ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + // FIXME we should pick tglob.dii_all_textures here directly .value = (vk_descriptor_value_t){ .image = { - .sampler = vk_core.default_sampler, + .sampler = tglob.default_sampler_fixme, .imageView = tglob.skybox_cube.vk.image.view ? tglob.skybox_cube.vk.image.view : tglob.cubemap_placeholder.vk.image.view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }, diff --git a/ref/vk/vk_textures.c b/ref/vk/vk_textures.c index ce0cee38..e9ab7f88 100644 --- a/ref/vk/vk_textures.c +++ b/ref/vk/vk_textures.c @@ -21,16 +21,19 @@ static vk_texture_t vk_textures[MAX_TEXTURES]; static vk_texture_t* vk_texturesHashTable[TEXTURES_HASH_SIZE]; static uint vk_numTextures; -vk_textures_global_t tglob; +vk_textures_global_t tglob = {0}; static void VK_CreateInternalTextures(void); +static VkSampler pickSamplerForFlags( texFlags_t flags ); -void initTextures( void ) -{ +void initTextures( void ) { memset( vk_textures, 0, sizeof( vk_textures )); memset( vk_texturesHashTable, 0, sizeof( vk_texturesHashTable )); vk_numTextures = 0; + tglob.default_sampler_fixme = pickSamplerForFlags(0); + ASSERT(tglob.default_sampler_fixme != VK_NULL_HANDLE); + // create unused 0-entry Q_strncpy( vk_textures->name, "*unused*", sizeof( vk_textures->name )); vk_textures->hashValue = COM_HashKey( vk_textures->name, TEXTURES_HASH_SIZE ); @@ -49,14 +52,19 @@ void initTextures( void ) gEngine.Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" ); */ + // Fill empty texture with references to the default texture { - const vk_texture_t *const default_texture = vk_textures + tglob.defaultTexture; + const VkImageView default_view = vk_textures[tglob.defaultTexture].vk.image.view; + ASSERT(default_view != VK_NULL_HANDLE); for (int i = 0; i < MAX_TEXTURES; ++i) { - const vk_texture_t *const tex = findTexture(i);; + const vk_texture_t *const tex = vk_textures + i; + if (tex->vk.image.view) + continue; + tglob.dii_all_textures[i] = (VkDescriptorImageInfo){ - .imageView = tex->vk.image.view != VK_NULL_HANDLE ? tex->vk.image.view : default_texture->vk.image.view, + .imageView = default_view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .sampler = vk_core.default_sampler, + .sampler = tglob.default_sampler_fixme, }; } } @@ -74,6 +82,11 @@ void destroyTextures( void ) XVK_ImageDestroy(&tglob.cubemap_placeholder.vk.image); memset(&tglob.cubemap_placeholder, 0, sizeof(tglob.cubemap_placeholder)); + for (int i = 0; i < ARRAYSIZE(tglob.samplers); ++i) { + if (tglob.samplers[i].sampler != VK_NULL_HANDLE) + vkDestroySampler(vk_core.device, tglob.samplers[i].sampler, NULL); + } + //memset( tglob.lightmapTextures, 0, sizeof( tglob.lightmapTextures )); memset( vk_texturesHashTable, 0, sizeof( vk_texturesHashTable )); memset( vk_textures, 0, sizeof( vk_textures )); @@ -479,6 +492,48 @@ static void BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, in } } +static VkSampler createSamplerForFlags( texFlags_t flags ) { + VkSampler sampler; + const VkFilter filter_mode = (flags & TF_NEAREST) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; + const VkSamplerAddressMode addr_mode = + (flags & TF_BORDER) ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER + : ((flags & TF_CLAMP) ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT); + const VkSamplerCreateInfo sci = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = filter_mode, + .minFilter = filter_mode, + .addressModeU = addr_mode, + .addressModeV = addr_mode, + .addressModeW = addr_mode, + .anisotropyEnable = vk_core.physical_device.anisotropy_enabled, + .maxAnisotropy = vk_core.physical_device.properties.limits.maxSamplerAnisotropy, + .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .minLod = 0.f, + .maxLod = 16., + }; + XVK_CHECK(vkCreateSampler(vk_core.device, &sci, NULL, &sampler)); + return sampler; +} + +static VkSampler pickSamplerForFlags( texFlags_t flags ) { + flags &= (TF_BORDER | TF_CLAMP | TF_NEAREST); + + for (int i = 0; i < ARRAYSIZE(tglob.samplers); ++i) { + if (tglob.samplers[i].sampler == VK_NULL_HANDLE) { + tglob.samplers[i].flags = flags; + return tglob.samplers[i].sampler = createSamplerForFlags(flags); + } + + if (tglob.samplers[i].flags == flags) + return tglob.samplers[i].sampler; + } + + gEngine.Con_Printf(S_ERROR "Couldn't find/allocate sampler for flags %x\n", flags); + return tglob.default_sampler_fixme; +} + static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap) { const VkFormat format = VK_GetFormat(layers[0]->type); int mipCount = 0; @@ -642,21 +697,25 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, // 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) - { - VkDescriptorImageInfo dii_tex = { + if (vk_desc.next_free != MAX_TEXTURES) { + 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){ .imageView = tex->vk.image.view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .sampler = pickSamplerForFlags( tex->flags ), }; - VkWriteDescriptorSet wds[] = { { + 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, + .pImageInfo = dii_tex, + .dstSet = tex->vk.descriptor = vk_desc.sets[vk_desc.next_free++], }}; - wds[0].dstSet = tex->vk.descriptor = vk_desc.sets[vk_desc.next_free++]; vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL); } else @@ -735,19 +794,6 @@ int VK_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) return 0; } - { - const int index = tex - vk_textures; - tglob.dii_all_textures[index] = (VkDescriptorImageInfo){ - .imageView = tex->vk.image.view, - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .sampler = vk_core.default_sampler, - }; - } - - /* FIXME - VK_ApplyTextureParams( tex ); // update texture filter, wrap etc - */ - tex->width = pic->width; tex->height = pic->height; @@ -842,12 +888,11 @@ void VK_FreeTexture( unsigned int texnum ) { tglob.dii_all_textures[texnum] = (VkDescriptorImageInfo){ .imageView = vk_textures[tglob.defaultTexture].vk.image.view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .sampler = vk_core.default_sampler, + .sampler = tglob.default_sampler_fixme, }; } -int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ) -{ +static int loadTextureFromBuffers( const char *name, rgbdata_t *const *const pic, int pic_count, texFlags_t flags, qboolean update ) { vk_texture_t *tex; if( !Common_CheckTexName( name )) @@ -872,30 +917,22 @@ int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags tex = Common_AllocTexture( name, flags ); } - VK_ProcessImage( tex, pic ); + for (int i = 0; i < pic_count; ++i) + VK_ProcessImage( tex, pic[i] ); - if( !uploadTexture( tex, &pic, 1, false )) + if( !uploadTexture( tex, pic, pic_count, false )) { memset( tex, 0, sizeof( vk_texture_t )); return 0; } - { - const int index = tex - vk_textures; - tglob.dii_all_textures[index] = (VkDescriptorImageInfo){ - .imageView = tex->vk.image.view, - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .sampler = vk_core.default_sampler, - }; - } - - /* FIXME - VK_ApplyTextureParams( tex ); // update texture filter, wrap etc - */ - return (tex - vk_textures); } +int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ) { + return loadTextureFromBuffers(name, &pic, 1, flags, update); +} + int XVK_TextureLookupF( const char *fmt, ...) { int tex_id = 0; char buffer[1024]; diff --git a/ref/vk/vk_textures.h b/ref/vk/vk_textures.h index 09f3fa1c..1a83c9c0 100644 --- a/ref/vk/vk_textures.h +++ b/ref/vk/vk_textures.h @@ -26,6 +26,8 @@ typedef struct vk_texture_s #define MAX_LIGHTMAPS 256 +#define MAX_SAMPLERS 8 // TF_NEAREST x 2 * TF_BORDER x 2 * TF_CLAMP x 2 + typedef struct vk_textures_global_s { int defaultTexture; // use for bad textures @@ -48,6 +50,14 @@ typedef struct vk_textures_global_s vk_texture_t cubemap_placeholder; VkDescriptorImageInfo dii_all_textures[MAX_TEXTURES]; + + // FIXME this should not exist, all textures should have their own samplers based on flags + VkSampler default_sampler_fixme; + + struct { + texFlags_t flags; + VkSampler sampler; + } samplers[MAX_SAMPLERS]; } vk_textures_global_t; // TODO rename this consistently