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
This commit is contained in:
Ivan Avdeev 2023-03-08 14:16:21 -08:00 committed by GitHub
parent 6d7fd41494
commit 01de5957d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 126 additions and 72 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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