vk: move things around between {r,vk}_texture

Another step in preparation for new hash table and better lifetime
management
This commit is contained in:
Ivan Avdeev 2023-10-24 11:32:42 -04:00
parent b016de0c83
commit c36080c982
13 changed files with 417 additions and 326 deletions

View File

@ -1,3 +1,27 @@
# 2023-10-24 E318
- [ ] use new hashmap for textures
- [ ] use vk_texure array directly as open addressing hash table
- [ ] Completely hide `struct vk_texture`
- [ ] just try
- [ ] texture indexes are no longer consecutive
- [ ] blue noise texture breaks => make it a separate (3d) thing
- [ ] index=0 is now valid
- I. mark 0 as occupied to avoid allocating it
- II. Increase all returned indexes by 1. Then dec it back wherever it is passed back
- (SAD): cannot make builtin textures have stable indexes anymore
# E313
## Pre-next:
- validation crash
## Next:
- KTX2 PR against upstream
- texture leaks
- better texture storage
- hash map
- texture lifetimes/refcounts
- texture deletion
- mass (for single device wait idle)
# Programmable render
- [ ] what if new meatpipe has different image format for a creatable image?
- [ ] implicit dependency tracking. pass defines:

View File

@ -7,6 +7,9 @@
#include "vk_logs.h"
#include "r_speeds.h"
#define PCG_IMPLEMENT
#include "pcg.h"
#include "xash3d_mathlib.h"
#include "crtlib.h"
#include "crclib.h" // COM_HashKey
@ -25,12 +28,24 @@ vk_texture_t vk_textures[MAX_TEXTURES];
vk_texture_t* vk_texturesHashTable[TEXTURES_HASH_SIZE];
uint vk_numTextures;
vk_textures_global_t tglob = {0};
extern vk_texture_t vk_textures[MAX_TEXTURES];
extern vk_texture_t* vk_texturesHashTable[TEXTURES_HASH_SIZE];
extern uint vk_numTextures;
static struct {
poolhandle_t mempool;
} g_textures;
// FIXME imported from vk_textures.h
qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint);
void unloadSkybox( void );
static void createDefaultTextures( void );
static int textureLoadFromFileF(int flags, colorspace_hint_e colorspace, const char *fmt, ...);
qboolean R_TexturesInit( void ) {
tglob.mempool = Mem_AllocPool( "vktextures" );
g_textures.mempool = Mem_AllocPool( "vktextures" );
memset( vk_textures, 0, sizeof( vk_textures ));
memset( vk_texturesHashTable, 0, sizeof( vk_texturesHashTable ));
@ -43,7 +58,12 @@ qboolean R_TexturesInit( void ) {
vk_texturesHashTable[vk_textures->hashValue] = vk_textures;
vk_numTextures = 1;
return R_VkTexturesInit();
createDefaultTextures();
if (!R_VkTexturesInit())
return false;
return true;
}
void R_TexturesShutdown( void )
@ -125,10 +145,10 @@ static vk_texture_t *Common_TextureForName( const char *name )
return NULL;
}
/* FIXME static*/ rgbdata_t *Common_FakeImage( int width, int height, int depth, int flags )
static rgbdata_t *Common_FakeImage( int width, int height, int depth, int flags )
{
// TODO: Fix texture and it's buffer leaking.
rgbdata_t *r_image = Mem_Malloc( tglob.mempool, sizeof( rgbdata_t ) );
rgbdata_t *r_image = Mem_Malloc( g_textures.mempool, sizeof( rgbdata_t ) );
// also use this for bad textures, but without alpha
r_image->width = Q_max( 1, width );
@ -140,7 +160,7 @@ static vk_texture_t *Common_TextureForName( const char *name )
r_image->size = r_image->width * r_image->height * r_image->depth * 4;
if( FBitSet( r_image->flags, IMAGE_CUBEMAP )) r_image->size *= 6;
r_image->buffer = Mem_Malloc( tglob.mempool, r_image->size);
r_image->buffer = Mem_Malloc( g_textures.mempool, r_image->size);
r_image->palette = NULL;
r_image->numMips = 1;
r_image->encode = 0;
@ -150,6 +170,151 @@ static vk_texture_t *Common_TextureForName( const char *name )
return r_image;
}
#define BLUE_NOISE_NAME_F "bluenoise/LDR_RGBA_%d.png"
static void generateFallbackNoiseTextures(void) {
pcg32_random_t pcg_state = {
BLUE_NOISE_SIZE * BLUE_NOISE_SIZE - 1,
17,
};
uint32_t scratch[BLUE_NOISE_SIZE * BLUE_NOISE_SIZE];
rgbdata_t pic = {
.width = BLUE_NOISE_SIZE,
.height = BLUE_NOISE_SIZE,
.depth = 1,
.flags = 0,
.type = PF_RGBA_32,
.size = BLUE_NOISE_SIZE * BLUE_NOISE_SIZE * 4,
.buffer = (byte*)&scratch,
.palette = NULL,
.numMips = 1,
.encode = 0,
};
int blueNoiseTexturesBegin = -1;
for (int i = 0; i < BLUE_NOISE_SIZE; ++i) {
for (int j = 0; j < COUNTOF(scratch); ++j) {
scratch[j] = pcg32_random_r(&pcg_state);
}
char name[256];
snprintf(name, sizeof(name), BLUE_NOISE_NAME_F, i);
const int texid = R_TextureUploadFromBufferNew(name, &pic, TF_NOMIPMAP);
ASSERT(texid > 0);
if (blueNoiseTexturesBegin == -1) {
ASSERT(texid == BLUE_NOISE_TEXTURE_ID);
blueNoiseTexturesBegin = texid;
} else {
ASSERT(blueNoiseTexturesBegin + i == texid);
}
}
}
static void loadBlueNoiseTextures(void) {
int blueNoiseTexturesBegin = -1;
for (int i = 0; i < 64; ++i) {
const int texid = textureLoadFromFileF(TF_NOMIPMAP, kColorspaceLinear, BLUE_NOISE_NAME_F, i);
if (blueNoiseTexturesBegin == -1) {
if (texid <= 0) {
ERR("Couldn't find precomputed blue noise textures. Generating bad quality regular noise textures as a fallback");
generateFallbackNoiseTextures();
return;
}
blueNoiseTexturesBegin = texid;
} else {
ASSERT(texid > 0);
ASSERT(blueNoiseTexturesBegin + i == texid);
}
}
INFO("Base blue noise texture is %d", blueNoiseTexturesBegin);
ASSERT(blueNoiseTexturesBegin == BLUE_NOISE_TEXTURE_ID);
}
static void createDefaultTextures( void )
{
int dx2, dy, d;
int x, y;
rgbdata_t *pic;
// emo-texture from quake1
pic = Common_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR );
for( y = 0; y < 16; y++ )
{
for( x = 0; x < 16; x++ )
{
if(( y < 8 ) ^ ( x < 8 ))
((uint *)pic->buffer)[y*16+x] = 0xFFFF00FF;
else ((uint *)pic->buffer)[y*16+x] = 0xFF000000;
}
}
tglob.defaultTexture = R_TextureUploadFromBufferNew( REF_DEFAULT_TEXTURE, pic, TF_COLORMAP );
// particle texture from quake1
pic = Common_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA );
for( x = 0; x < 16; x++ )
{
dx2 = x - 8;
dx2 = dx2 * dx2;
for( y = 0; y < 16; y++ )
{
dy = y - 8;
d = 255 - 35 * sqrt( dx2 + dy * dy );
pic->buffer[( y * 16 + x ) * 4 + 3] = bound( 0, d, 255 );
}
}
tglob.particleTexture = R_TextureUploadFromBufferNew( REF_PARTICLE_TEXTURE, pic, TF_CLAMP );
// white texture
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFFFFFFFF;
tglob.whiteTexture = R_TextureUploadFromBufferNew( REF_WHITE_TEXTURE, pic, TF_COLORMAP );
// gray texture
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFF7F7F7F;
tglob.grayTexture = R_TextureUploadFromBufferNew( REF_GRAY_TEXTURE, pic, TF_COLORMAP );
// black texture
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFF000000;
tglob.blackTexture = R_TextureUploadFromBufferNew( REF_BLACK_TEXTURE, pic, TF_COLORMAP );
// cinematic dummy
pic = Common_FakeImage( 640, 100, 1, IMAGE_HAS_COLOR );
tglob.cinTexture = R_TextureUploadFromBufferNew( "*cintexture", pic, TF_NOMIPMAP|TF_CLAMP );
{
rgbdata_t *sides[6];
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFFFFFFFF;
sides[0] = pic;
sides[1] = pic;
sides[2] = pic;
sides[3] = pic;
sides[4] = pic;
sides[5] = pic;
R_VkTexturesSkyboxUpload( "skybox_placeholder", sides, kColorspaceGamma, true );
}
loadBlueNoiseTextures();
}
/*
===============
GL_ProcessImage
@ -428,7 +593,7 @@ const char* R_TextureGetNameByIndex( unsigned int texnum )
return vk_textures[texnum].name;
}
/* FIXME static */ int loadTextureInternal( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint, qboolean force_update ) {
static int loadTextureInternal( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint, qboolean force_update ) {
if( !Common_CheckTexName( name ))
return 0;
@ -462,7 +627,7 @@ const char* R_TextureGetNameByIndex( unsigned int texnum )
// upload texture
VK_ProcessImage( tex, pic );
if( !uploadTexture( tex, &pic, 1, false, colorspace_hint ))
if( !R_VkTextureUpload( tex, &pic, 1, false, colorspace_hint ))
{
// FIXME remove from hash table
memset( tex, 0, sizeof( vk_texture_t ));
@ -484,6 +649,22 @@ int R_TextureUploadFromFile( const char *name, const byte *buf, size_t size, int
return loadTextureInternal(name, buf, size, flags, kColorspaceGamma, force_update);
}
int R_TextureUploadFromFileEx( const char *filename, colorspace_hint_e colorspace, qboolean force_reload) {
return loadTextureInternal( filename, NULL, 0, 0, colorspace, force_reload );
}
static int textureLoadFromFileF(int flags, colorspace_hint_e colorspace, const char *fmt, ...) {
int tex_id = 0;
char buffer[1024];
va_list argptr;
va_start( argptr, fmt );
vsnprintf( buffer, sizeof buffer, fmt, argptr );
va_end( argptr );
const qboolean force_update = false;
return loadTextureInternal(buffer, NULL, 0, flags, colorspace, force_update);
}
void R_TextureFree( unsigned int texnum ) {
// FIXME this is incorrect and leads to missing textures
R_TextureRelease( texnum );
@ -518,7 +699,7 @@ static int loadTextureFromBuffers( const char *name, rgbdata_t *const *const pic
for (int i = 0; i < pic_count; ++i)
VK_ProcessImage( tex, pic[i] );
if( !uploadTexture( tex, pic, pic_count, false, kColorspaceGamma ))
if( !R_VkTextureUpload( tex, pic, pic_count, false, kColorspaceGamma ))
{
memset( tex, 0, sizeof( vk_texture_t ));
return 0;
@ -619,8 +800,8 @@ static qboolean loadSkybox( const char *prefix, int style ) {
if( !Common_CheckTexName( prefix ))
goto cleanup;
Q_strncpy( tglob.skybox_cube.name, prefix, sizeof( tglob.skybox_cube.name ));
success = uploadTexture(&tglob.skybox_cube, sides, 6, true, kColorspaceGamma);
R_VkTexturesSkyboxUpload( prefix, sides, kColorspaceGamma, false );
cleanup:
for (int j = 0; j < i; ++j)
@ -630,7 +811,6 @@ cleanup:
tglob.fCustomSkybox = true;
DEBUG( "Skybox done" );
} else {
tglob.skybox_cube.name[0] = '\0';
ERR( "Skybox failed" );
unloadSkybox();
}
@ -649,7 +829,7 @@ void R_TextureSetupSky( const char *skyboxname ) {
}
for (int i = 0; i < ARRAYSIZE(skybox_prefixes); ++i) {
char loadname[MAX_STRING];
char loadname[MAX_STRING];
int style, len;
Q_snprintf( loadname, sizeof( loadname ), skybox_prefixes[i], skyboxname );
@ -666,10 +846,10 @@ void R_TextureSetupSky( const char *skyboxname ) {
return;
}
// Try default skybox if failed
if (Q_stricmp(skyboxname, skybox_default) != 0) {
WARN("missed or incomplete skybox '%s'", skyboxname);
// FIXME infinite recursion
R_TextureSetupSky( "desert" ); // force to default
WARN("missed or incomplete skybox '%s', trying default '%s'", skyboxname, skybox_default);
R_TextureSetupSky( skybox_default );
}
}
@ -729,3 +909,40 @@ int R_TextureCreateDummy_FIXME( const char *name ) {
return R_TextureUploadFromBufferNew(name, pic, TF_NOMIPMAP);
}
vk_texture_t *R_TextureGetByIndex(int index)
{
ASSERT(index >= 0);
ASSERT(index < MAX_TEXTURES);
//return g_vktextures.textures + index;
return vk_textures + index;
}
int R_TexturesGetParm( int parm, int arg ) {
const vk_texture_t *const tex = R_TextureGetByIndex( arg );
switch(parm){
case PARM_TEX_WIDTH:
case PARM_TEX_SRC_WIDTH: // TODO why is this separate?
return tex->width;
case PARM_TEX_HEIGHT:
case PARM_TEX_SRC_HEIGHT:
return tex->height;
case PARM_TEX_FLAGS:
return tex->flags;
// TODO
case PARM_TEX_SKYBOX:
case PARM_TEX_SKYTEXNUM:
case PARM_TEX_LIGHTMAP:
case PARM_TEX_TARGET:
case PARM_TEX_TEXNUM:
case PARM_TEX_DEPTH:
case PARM_TEX_GLFORMAT:
case PARM_TEX_ENCODE:
case PARM_TEX_MIPCOUNT:
case PARM_TEX_MEMORY:
return 0;
default:
return 0;
}
}

View File

@ -5,10 +5,47 @@
#include "com_model.h" // required for ref_api.h
#include "ref_api.h" // texFlags_t
#define MAX_LIGHTMAPS 256
typedef struct vk_textures_global_s
{
// TODO Fix these at compile time statically, akin to BLUE_NOISE_TEXTURE_ID
int defaultTexture; // use for bad textures
int particleTexture;
int whiteTexture;
int grayTexture;
int blackTexture;
int solidskyTexture; // quake1 solid-sky layer
int alphaskyTexture; // quake1 alpha-sky layer
int lightmapTextures[MAX_LIGHTMAPS];
int dlightTexture; // custom dlight texture
int cinTexture; // cinematic texture
// Hardcoded expected blue noise texture slot
// TODO consider moving it into a separate resource bindable by request
// TODO make it a 3D texture. Currently it's just a sequence of BLUE_NOISE_SIZE textures, loaded into consecutive slots.
#define BLUE_NOISE_TEXTURE_ID 7
// Hardcode blue noise texture size to 64x64x64
#define BLUE_NOISE_SIZE 64
// TODO wire it up for ref_interface_t return
qboolean fCustomSkybox;
// TODO:
//1. vk_texture_t blue_noise; 3d texture
//2. separate binding similar to skybox in vk_rtx.c and shaders
//3. patch shader function
} vk_textures_global_t;
// TODO rename this consistently
extern vk_textures_global_t tglob;
qboolean R_TexturesInit( void );
void R_TexturesShutdown( void );
// Ref interface functions, exported
// TODO mark names somehow, ie. R_TextureApi... ?
int R_TextureFindByName( const char *name );
const char* R_TextureGetNameByIndex( unsigned int texnum );
@ -18,4 +55,13 @@ int R_TextureUploadFromFile( const char *name, const byte *buf, size_t size, int
int R_TextureUploadFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update_only );
void R_TextureFree( unsigned int texnum );
#include "vk_textures.h"
int R_TexturesGetParm( int parm, int arg );
// Extra functions used in ref_vk
int R_TextureFindByNameF( const char *fmt, ...);
// Tries to find a texture by its short name
// Full names depend on map name, wad name, etc. This function tries them all.
// Returns -1 if not found
int R_TextureFindByNameLike( const char *texture_name );

View File

@ -80,6 +80,7 @@ int urmomInsert(const urmom_desc_t* desc, const char *key) {
if (URMOM_IS_OCCUPIED(*hdr)) {
if (hdr->hash == hash && strcmp(key, hdr->key) == 0)
// Return existing item
return index;
} else
// Reached the end of occupied chain, return the available slot

View File

@ -582,7 +582,8 @@ static void brushDrawWater(vk_brush_model_t *bmodel, const cl_entity_t *ent, int
APROF_SCOPE_END(brush_draw_water);
}
// FIXME use this
#if 0
// TODO use this
static void computeConveyorSpeed(const color24 rendercolor, int tex_index, vec2_t speed) {
float sy, cy;
float flConveyorSpeed = 0.0f;
@ -610,6 +611,7 @@ static void computeConveyorSpeed(const color24 rendercolor, int tex_index, vec2_
speed[0] = cy * flRate;
speed[1] = sy * flRate;
}
#endif
/*
===============
@ -644,9 +646,10 @@ const texture_t *R_TextureAnimation( const cl_entity_t *ent, const msurface_t *s
int speed;
// Quake1 textures uses 10 frames per second
/* TODO
if( FBitSet( R_TextureGetByIndex( base->gl_texturenum )->flags, TF_QUAKEPAL ))
speed = 10;
else speed = 20;
else */ speed = 20;
reletive = (int)(gpGlobals->time * speed) % base->anim_total;
}
@ -1566,7 +1569,7 @@ void VK_BrushUnloadTextures( model_t *mod )
if( !tx || tx->gl_texturenum == tglob.defaultTexture )
continue; // free slot
R_TextureRelease( tx->gl_texturenum ); // main texture
R_TextureRelease( tx->fb_texturenum ); // luma texture
R_TextureFree( tx->gl_texturenum ); // main texture
R_TextureFree( tx->fb_texturenum ); // luma texture
}
}

View File

@ -27,7 +27,7 @@ struct xvk_image_s;
typedef union {
VkDescriptorBufferInfo buffer;
VkDescriptorImageInfo image;
VkDescriptorImageInfo *image_array;
const VkDescriptorImageInfo *image_array;
VkWriteDescriptorSetAccelerationStructureKHR accel;
const struct r_vk_image_s *image_object;
} vk_descriptor_value_t;

View File

@ -1,5 +1,5 @@
#include "vk_materials.h"
#include "r_textures.h"
#include "vk_textures.h"
#include "vk_mapents.h"
#include "vk_const.h"
#include "profiler.h"

View File

@ -3,7 +3,7 @@
#include "vk_buffer.h"
#include "vk_core.h"
#include "vk_common.h"
#include "r_textures.h"
#include "vk_textures.h"
#include "vk_framectl.h"
#include "vk_renderstate.h"
#include "vk_pipeline.h"
@ -264,12 +264,12 @@ static void drawOverlay( VkCommandBuffer cmdbuf ) {
for (int i = 0; i < g2d.batch_count && g2d.batch[i].vertex_count > 0; ++i)
{
vk_texture_t *texture = R_TextureGetByIndex(g2d.batch[i].texture);
const VkDescriptorSet tex_unorm = R_VkTextureGetDescriptorUnorm( g2d.batch[i].texture );
const VkPipeline pipeline = g2d.pipelines[g2d.batch[i].blending_mode];
if (texture->vk.descriptor_unorm)
if (tex_unorm)
{
vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g2d.pipeline_layout, 0, 1, &texture->vk.descriptor_unorm, 0, NULL);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g2d.pipeline_layout, 0, 1, &tex_unorm, 0, NULL);
vkCmdDraw(cmdbuf, g2d.batch[i].vertex_count, 1, g2d.batch[i].vertex_offset, 0);
} // FIXME else what?
}

View File

@ -7,7 +7,7 @@
#include "vk_const.h"
#include "vk_common.h"
#include "vk_pipeline.h"
#include "r_textures.h"
#include "vk_textures.h"
#include "vk_math.h"
#include "vk_rtx.h"
#include "vk_descriptor.h"
@ -589,14 +589,16 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw )
if (lightmap != draw->draw.lightmap) {
lightmap = draw->draw.lightmap;
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 2, 1, &R_TextureGetByIndex(lightmap)->vk.descriptor_unorm, 0, NULL);
const VkDescriptorSet lm_unorm = R_VkTextureGetDescriptorUnorm(lightmap);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 2, 1, &lm_unorm, 0, NULL);
}
if (texture != draw->draw.texture)
{
texture = draw->draw.texture;
const VkDescriptorSet tex_unorm = R_VkTextureGetDescriptorUnorm(texture);
// TODO names/enums for binding points
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 1, 1, &R_TextureGetByIndex(texture)->vk.descriptor_unorm, 0, NULL);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 1, 1, &tex_unorm, 0, NULL);
}
// Only indexed mode is supported

View File

@ -245,20 +245,26 @@ static const char *getParmName(int parm)
static int VK_RefGetParm( int parm, int arg )
{
vk_texture_t *tex = NULL;
// TODO all PARM_TEX handle in r_texture internally
switch(parm){
case PARM_TEX_WIDTH:
case PARM_TEX_SRC_WIDTH: // TODO why is this separate?
tex = R_TextureGetByIndex(arg);
return tex->width;
case PARM_TEX_HEIGHT:
case PARM_TEX_SRC_WIDTH: // TODO why is this separate?
case PARM_TEX_SRC_HEIGHT:
tex = R_TextureGetByIndex(arg);
return tex->height;
case PARM_TEX_FLAGS:
tex = R_TextureGetByIndex(arg);
return tex->flags;
/* TODO
case PARM_TEX_SKYBOX:
case PARM_TEX_SKYTEXNUM:
case PARM_TEX_LIGHTMAP:
case PARM_TEX_TARGET:
case PARM_TEX_TEXNUM:
case PARM_TEX_DEPTH:
case PARM_TEX_GLFORMAT:
case PARM_TEX_ENCODE:
case PARM_TEX_MIPCOUNT:
case PARM_TEX_MEMORY:
*/
return R_TexturesGetParm( parm, arg );
case PARM_MODERNFLASHLIGHT:
if (CVAR_TO_BOOL( vk_rtx )) {
return true;

View File

@ -14,7 +14,7 @@
#include "vk_pipeline.h"
#include "vk_ray_internal.h"
#include "vk_staging.h"
#include "r_textures.h"
#include "vk_textures.h"
#include "vk_combuf.h"
#include "vk_logs.h"
@ -610,7 +610,7 @@ qboolean VK_RayInit( void )
g_rtx.res[ExternalResource_textures].resource = (vk_resource_t){
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.value = (vk_descriptor_value_t){
.image_array = tglob.dii_all_textures,
.image_array = R_VkTexturesGetAllDescriptorsArray(),
}
};
g_rtx.res[ExternalResource_textures].refcount = 1;

View File

@ -10,21 +10,17 @@
#include "alolcator.h"
#include "profiler.h"
#include "xash3d_mathlib.h" // bound
#define PCG_IMPLEMENT
#include "pcg.h"
#include "ktx2.h"
#include <math.h> // sqrt
vk_textures_global_t tglob = {0};
#define LOG_MODULE LogModule_Textures
#define MODULE_NAME "textures"
#define MAX_SAMPLERS 8 // TF_NEAREST x 2 * TF_BORDER x 2 * TF_CLAMP x 2
static struct {
struct {
int count;
@ -38,11 +34,15 @@ static struct {
VkSampler default_sampler;
vk_texture_t cubemap_placeholder;
//vk_texture_t textures[MAX_TEXTURES];
//alo_int_pool_t textures_free;
} g_textures;
// All textures descriptors in their native formats used for RT
VkDescriptorImageInfo dii_all_textures[MAX_TEXTURES];
vk_texture_t skybox_cube;
vk_texture_t cubemap_placeholder;
} g_vktextures;
#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2)
@ -55,34 +55,28 @@ int CalcMipmapCount( vk_texture_t *tex, qboolean haveBuffer );
qboolean validatePicLayers(const char* const name, rgbdata_t *const *const layers, int num_layers);
void BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, int flags );
static void VK_CreateInternalTextures(void);
static VkSampler pickSamplerForFlags( texFlags_t flags );
static void textureDestroyVkImage( vk_texture_t *tex );
// FIXME should be static
void unloadSkybox( void );
qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint);
/* FIXME static */ int loadTextureInternal( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint, qboolean force_update );
/* FIXME static */ rgbdata_t *Common_FakeImage( int width, int height, int depth, int flags );
/* FIXME static */ qboolean Common_CheckTexName( const char *name );
qboolean R_VkTextureUpload(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint);
qboolean R_VkTexturesInit( void ) {
R_SPEEDS_METRIC(g_textures.stats.count, "count", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_textures.stats.size_total, "size_total", kSpeedsMetricBytes);
R_SPEEDS_METRIC(g_vktextures.stats.count, "count", kSpeedsMetricCount);
R_SPEEDS_METRIC(g_vktextures.stats.size_total, "size_total", kSpeedsMetricBytes);
// TODO really check device caps for this
gEngine.Image_AddCmdFlags( IL_DDS_HARDWARE | IL_KTX2_RAW );
g_textures.default_sampler = pickSamplerForFlags(0);
ASSERT(g_textures.default_sampler != VK_NULL_HANDLE);
g_vktextures.default_sampler = pickSamplerForFlags(0);
ASSERT(g_vktextures.default_sampler != VK_NULL_HANDLE);
/* FIXME
// validate cvars
R_SetTextureParameters();
*/
VK_CreateInternalTextures();
/* FIXME
gEngine.Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" );
*/
@ -96,10 +90,10 @@ qboolean R_VkTexturesInit( void ) {
if (tex->vk.image.view)
continue;
tglob.dii_all_textures[i] = (VkDescriptorImageInfo){
g_vktextures.dii_all_textures[i] = (VkDescriptorImageInfo){
.imageView = default_view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.sampler = g_textures.default_sampler,
.sampler = g_vktextures.default_sampler,
};
}
}
@ -107,192 +101,27 @@ qboolean R_VkTexturesInit( void ) {
return true;
}
static void textureDestroy( int index );
static void textureDestroy( unsigned int index );
void R_VkTexturesShutdown( void ) {
/* for( unsigned int i = 0; i < COUNTOF(g_textures.textures); i++ ) */
/* for( unsigned int i = 0; i < COUNTOF(g_vktextures.textures); i++ ) */
/* textureDestroy( i ); */
unloadSkybox();
R_VkImageDestroy(&g_textures.cubemap_placeholder.vk.image);
g_textures.stats.size_total -= g_textures.cubemap_placeholder.total_size;
g_textures.stats.count--;
memset(&g_textures.cubemap_placeholder, 0, sizeof(g_textures.cubemap_placeholder));
textureDestroyVkImage(&g_vktextures.cubemap_placeholder);
for (int i = 0; i < COUNTOF(g_textures.samplers); ++i) {
if (g_textures.samplers[i].sampler != VK_NULL_HANDLE)
vkDestroySampler(vk_core.device, g_textures.samplers[i].sampler, NULL);
// FIXME g_vktextures.stats.size_total -= g_vktextures.cubemap_placeholder.total_size;
g_vktextures.stats.count--;
for (int i = 0; i < COUNTOF(g_vktextures.samplers); ++i) {
if (g_vktextures.samplers[i].sampler != VK_NULL_HANDLE)
vkDestroySampler(vk_core.device, g_vktextures.samplers[i].sampler, NULL);
}
}
vk_texture_t *R_TextureGetByIndex(int index)
{
ASSERT(index >= 0);
ASSERT(index < MAX_TEXTURES);
//return g_textures.textures + index;
return vk_textures + index;
}
static int textureLoadFromFileF(int flags, colorspace_hint_e colorspace, const char *fmt, ...) {
int tex_id = 0;
char buffer[1024];
va_list argptr;
va_start( argptr, fmt );
vsnprintf( buffer, sizeof buffer, fmt, argptr );
va_end( argptr );
const qboolean force_update = false;
return loadTextureInternal(buffer, NULL, 0, flags, colorspace, force_update);
}
#define BLUE_NOISE_NAME_F "bluenoise/LDR_RGBA_%d.png"
static qboolean generateFallbackNoiseTextures(void) {
pcg32_random_t pcg_state = {
BLUE_NOISE_SIZE * BLUE_NOISE_SIZE - 1,
17,
};
uint32_t scratch[BLUE_NOISE_SIZE * BLUE_NOISE_SIZE];
rgbdata_t pic = {
.width = BLUE_NOISE_SIZE,
.height = BLUE_NOISE_SIZE,
.depth = 1,
.flags = 0,
.type = PF_RGBA_32,
.size = BLUE_NOISE_SIZE * BLUE_NOISE_SIZE * 4,
.buffer = (byte*)&scratch,
.palette = NULL,
.numMips = 1,
.encode = 0,
};
int blueNoiseTexturesBegin = -1;
for (int i = 0; i < BLUE_NOISE_SIZE; ++i) {
for (int j = 0; j < COUNTOF(scratch); ++j) {
scratch[j] = pcg32_random_r(&pcg_state);
}
char name[256];
snprintf(name, sizeof(name), BLUE_NOISE_NAME_F, i);
const int texid = R_TextureUploadFromBufferNew(name, &pic, TF_NOMIPMAP);
ASSERT(texid > 0);
if (blueNoiseTexturesBegin == -1) {
ASSERT(texid == BLUE_NOISE_TEXTURE_ID);
blueNoiseTexturesBegin = texid;
} else {
ASSERT(blueNoiseTexturesBegin + i == texid);
}
}
return true;
}
static qboolean loadBlueNoiseTextures(void) {
int blueNoiseTexturesBegin = -1;
for (int i = 0; i < 64; ++i) {
const int texid = textureLoadFromFileF(TF_NOMIPMAP, kColorspaceLinear, BLUE_NOISE_NAME_F, i);
if (blueNoiseTexturesBegin == -1) {
if (texid <= 0) {
ERR("Couldn't find precomputed blue noise textures. Generating bad quality regular noise textures as a fallback");
return generateFallbackNoiseTextures();
}
blueNoiseTexturesBegin = texid;
} else {
ASSERT(texid > 0);
ASSERT(blueNoiseTexturesBegin + i == texid);
}
}
INFO("Base blue noise texture is %d", blueNoiseTexturesBegin);
ASSERT(blueNoiseTexturesBegin == BLUE_NOISE_TEXTURE_ID);
return true;
}
static void VK_CreateInternalTextures( void )
{
int dx2, dy, d;
int x, y;
rgbdata_t *pic;
// emo-texture from quake1
pic = Common_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR );
for( y = 0; y < 16; y++ )
{
for( x = 0; x < 16; x++ )
{
if(( y < 8 ) ^ ( x < 8 ))
((uint *)pic->buffer)[y*16+x] = 0xFFFF00FF;
else ((uint *)pic->buffer)[y*16+x] = 0xFF000000;
}
}
tglob.defaultTexture = R_TextureUploadFromBufferNew( REF_DEFAULT_TEXTURE, pic, TF_COLORMAP );
// particle texture from quake1
pic = Common_FakeImage( 16, 16, 1, IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA );
for( x = 0; x < 16; x++ )
{
dx2 = x - 8;
dx2 = dx2 * dx2;
for( y = 0; y < 16; y++ )
{
dy = y - 8;
d = 255 - 35 * sqrt( dx2 + dy * dy );
pic->buffer[( y * 16 + x ) * 4 + 3] = bound( 0, d, 255 );
}
}
tglob.particleTexture = R_TextureUploadFromBufferNew( REF_PARTICLE_TEXTURE, pic, TF_CLAMP );
// white texture
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFFFFFFFF;
tglob.whiteTexture = R_TextureUploadFromBufferNew( REF_WHITE_TEXTURE, pic, TF_COLORMAP );
// gray texture
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFF7F7F7F;
tglob.grayTexture = R_TextureUploadFromBufferNew( REF_GRAY_TEXTURE, pic, TF_COLORMAP );
// black texture
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFF000000;
tglob.blackTexture = R_TextureUploadFromBufferNew( REF_BLACK_TEXTURE, pic, TF_COLORMAP );
// cinematic dummy
pic = Common_FakeImage( 640, 100, 1, IMAGE_HAS_COLOR );
tglob.cinTexture = R_TextureUploadFromBufferNew( "*cintexture", pic, TF_NOMIPMAP|TF_CLAMP );
{
rgbdata_t *sides[6];
pic = Common_FakeImage( 4, 4, 1, IMAGE_HAS_COLOR );
for( x = 0; x < 16; x++ )
((uint *)pic->buffer)[x] = 0xFFFFFFFF;
sides[0] = pic;
sides[1] = pic;
sides[2] = pic;
sides[3] = pic;
sides[4] = pic;
sides[5] = pic;
uploadTexture( &g_textures.cubemap_placeholder, sides, 6, true, kColorspaceGamma );
}
loadBlueNoiseTextures();
}
static VkFormat VK_GetFormat(pixformat_t format, colorspace_hint_e colorspace_hint ) {
switch(format)
{
@ -377,18 +206,18 @@ static VkSampler createSamplerForFlags( texFlags_t flags ) {
static VkSampler pickSamplerForFlags( texFlags_t flags ) {
flags &= (TF_BORDER | TF_CLAMP | TF_NEAREST);
for (int i = 0; i < COUNTOF(g_textures.samplers); ++i) {
if (g_textures.samplers[i].sampler == VK_NULL_HANDLE) {
g_textures.samplers[i].flags = flags;
return g_textures.samplers[i].sampler = createSamplerForFlags(flags);
for (int i = 0; i < COUNTOF(g_vktextures.samplers); ++i) {
if (g_vktextures.samplers[i].sampler == VK_NULL_HANDLE) {
g_vktextures.samplers[i].flags = flags;
return g_vktextures.samplers[i].sampler = createSamplerForFlags(flags);
}
if (g_textures.samplers[i].flags == flags)
return g_textures.samplers[i].sampler;
if (g_vktextures.samplers[i].flags == flags)
return g_vktextures.samplers[i].sampler;
}
ERR("Couldn't find/allocate sampler for flags %x", flags);
return g_textures.default_sampler;
return g_vktextures.default_sampler;
}
static void setDescriptorSet(vk_texture_t* const tex, colorspace_hint_e colorspace_hint) {
@ -412,7 +241,7 @@ static void setDescriptorSet(vk_texture_t* const tex, colorspace_hint_e colorspa
};
// Set descriptor for bindless/ray tracing
tglob.dii_all_textures[index] = dii;
g_vktextures.dii_all_textures[index] = dii;
// Continue with setting unorm descriptor for traditional renderer
@ -532,11 +361,11 @@ static qboolean uploadRawKtx2( vk_texture_t *tex, const rgbdata_t* pic ) {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.sampler = pickSamplerForFlags( tex->flags ),
};
tglob.dii_all_textures[index] = dii;
g_vktextures.dii_all_textures[index] = dii;
}
g_textures.stats.size_total += tex->total_size;
g_textures.stats.count++;
g_vktextures.stats.size_total += tex->total_size;
g_vktextures.stats.count++;
tex->width = header->pixelWidth;
tex->height = header->pixelHeight;
@ -544,7 +373,7 @@ static qboolean uploadRawKtx2( vk_texture_t *tex, const rgbdata_t* pic ) {
return true;
}
qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint) {
qboolean R_VkTextureUpload(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint) {
tex->total_size = 0;
if (num_layers == 1 && layers[0]->type == PF_KTX2_RAW) {
@ -624,19 +453,11 @@ qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int nu
setDescriptorSet(tex, colorspace_hint);
g_textures.stats.size_total += tex->total_size;
g_textures.stats.count++;
g_vktextures.stats.size_total += tex->total_size;
g_vktextures.stats.count++;
return true;
}
int R_TextureUploadFromFileEx( const char *filename, colorspace_hint_e colorspace, qboolean force_reload) {
vk_texture_t *tex;
if( !Common_CheckTexName( filename ))
return 0;
return loadTextureInternal( filename, NULL, 0, 0, colorspace, force_reload );
}
static void textureDestroyVkImage( vk_texture_t *tex ) {
// Need to make sure that there are no references to this texture anywhere.
// It might have been added to staging and then immediately deleted, leaving references to its vkimage
@ -645,8 +466,8 @@ static void textureDestroyVkImage( vk_texture_t *tex ) {
XVK_CHECK(vkDeviceWaitIdle(vk_core.device));
R_VkImageDestroy(&tex->vk.image);
g_textures.stats.size_total -= tex->total_size;
g_textures.stats.count--;
g_vktextures.stats.size_total -= tex->total_size;
g_vktextures.stats.count--;
tex->total_size = 0;
}
@ -717,11 +538,9 @@ end:
}
void unloadSkybox( void ) {
if (tglob.skybox_cube.vk.image.image) {
R_VkImageDestroy(&tglob.skybox_cube.vk.image);
g_textures.stats.size_total -= tglob.skybox_cube.total_size;
g_textures.stats.count--;
memset(&tglob.skybox_cube, 0, sizeof(tglob.skybox_cube));
if (g_vktextures.skybox_cube.vk.image.image) {
textureDestroyVkImage( &g_vktextures.skybox_cube );
memset(&g_vktextures.skybox_cube, 0, sizeof(g_vktextures.skybox_cube));
}
tglob.fCustomSkybox = false;
@ -737,10 +556,25 @@ void R_TextureAcquire( unsigned int texnum ) {
VkDescriptorImageInfo R_VkTextureGetSkyboxDescriptorImageInfo( void ) {
return (VkDescriptorImageInfo){
.sampler = g_textures.default_sampler,
.imageView = tglob.skybox_cube.vk.image.view
? tglob.skybox_cube.vk.image.view
: g_textures.cubemap_placeholder.vk.image.view,
.sampler = g_vktextures.default_sampler,
.imageView = g_vktextures.skybox_cube.vk.image.view
? g_vktextures.skybox_cube.vk.image.view
: g_vktextures.cubemap_placeholder.vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
}
qboolean R_VkTexturesSkyboxUpload( const char *name, rgbdata_t *const sides[6], colorspace_hint_e colorspace_hint, qboolean placeholder) {
vk_texture_t *const dest = placeholder ? &g_vktextures.cubemap_placeholder : &g_vktextures.skybox_cube;
Q_strncpy( dest->name, name, sizeof( dest->name ));
return R_VkTextureUpload(dest, sides, 6, true, colorspace_hint);
}
VkDescriptorSet R_VkTextureGetDescriptorUnorm( uint index ) {
ASSERT( index < MAX_TEXTURES );
return vk_textures[index].vk.descriptor_unorm;
}
const VkDescriptorImageInfo* R_VkTexturesGetAllDescriptorsArray( void ) {
return g_vktextures.dii_all_textures;
}

View File

@ -1,4 +1,7 @@
#pragma once
#include "r_textures.h"
#include "vk_core.h"
#include "vk_image.h"
#include "vk_const.h"
@ -27,52 +30,18 @@ typedef struct vk_texture_s
// int used_maps_ago;
} vk_texture_t;
#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
{
poolhandle_t mempool;
// TODO Fix these at compile time statically, akin to BLUE_NOISE_TEXTURE_ID
int defaultTexture; // use for bad textures
int particleTexture;
int whiteTexture;
int grayTexture;
int blackTexture;
int solidskyTexture; // quake1 solid-sky layer
int alphaskyTexture; // quake1 alpha-sky layer
int lightmapTextures[MAX_LIGHTMAPS];
int dlightTexture; // custom dlight texture
int cinTexture; // cinematic texture
// Hardcoded expected blue noise texture slot
// TODO consider moving it into a separate resource bindable by request
// TODO make it a 3D texture. Currently it's just a sequence of BLUE_NOISE_SIZE textures, loaded into consecutive slots.
#define BLUE_NOISE_TEXTURE_ID 7
// Hardcode blue noise texture size to 64x64x64
#define BLUE_NOISE_SIZE 64
// TODO wire it up for ref_interface_t return
qboolean fCustomSkybox;
// TODO move to vk_textures/g_textures
vk_texture_t skybox_cube;
// All textures descriptors in their native formats used for RT
VkDescriptorImageInfo dii_all_textures[MAX_TEXTURES];
} vk_textures_global_t;
// TODO rename this consistently
extern vk_textures_global_t tglob;
typedef enum {
kColorspaceNative,
kColorspaceLinear,
kColorspaceGamma,
} colorspace_hint_e;
qboolean R_VkTexturesInit( void );
void R_VkTexturesShutdown( void );
// Functions only used in this renderer
vk_texture_t *R_TextureGetByIndex( int index );
qboolean R_VkTexturesSkyboxUpload( const char *name, rgbdata_t *const sides[6], colorspace_hint_e colorspace_hint, qboolean placeholder);
qboolean R_VkTextureUpload(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint);
// FIXME s/R_/R_Vk/
void R_TextureAcquire( unsigned int texnum );
@ -80,21 +49,10 @@ void R_TextureRelease( unsigned int texnum );
#define R_TextureUploadFromBufferNew(name, pic, flags) R_TextureUploadFromBuffer(name, pic, flags, false)
typedef enum {
kColorspaceNative,
kColorspaceLinear,
kColorspaceGamma,
} colorspace_hint_e;
int R_TextureUploadFromFileEx( const char *filename, colorspace_hint_e colorspace, qboolean force_reload );
int R_TextureFindByNameF( const char *fmt, ...);
// Tries to find a texture by its short name
// Full names depend on map name, wad name, etc. This function tries them all.
// Returns -1 if not found
int R_TextureFindByNameLike( const char *texture_name );
// Used by materials to piggy-back onto texture name-to-index hash table
int R_TextureCreateDummy_FIXME( const char *name );
VkDescriptorImageInfo R_VkTextureGetSkyboxDescriptorImageInfo( void );
const VkDescriptorImageInfo* R_VkTexturesGetAllDescriptorsArray( void );
VkDescriptorSet R_VkTextureGetDescriptorUnorm( uint index );