diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index a3f9a484..452c3c3a 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -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: diff --git a/ref/vk/r_textures.c b/ref/vk/r_textures.c index 26f14cc4..cb176b03 100644 --- a/ref/vk/r_textures.c +++ b/ref/vk/r_textures.c @@ -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; + } +} diff --git a/ref/vk/r_textures.h b/ref/vk/r_textures.h index 8c463672..a7311c3b 100644 --- a/ref/vk/r_textures.h +++ b/ref/vk/r_textures.h @@ -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 ); diff --git a/ref/vk/unordered_roadmap.c b/ref/vk/unordered_roadmap.c index c5e7f9cc..36c4bacf 100644 --- a/ref/vk/unordered_roadmap.c +++ b/ref/vk/unordered_roadmap.c @@ -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 diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index d8309815..14d1004f 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -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 } } diff --git a/ref/vk/vk_descriptor.h b/ref/vk/vk_descriptor.h index f54d1666..f9a39e07 100644 --- a/ref/vk/vk_descriptor.h +++ b/ref/vk/vk_descriptor.h @@ -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; diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index e430dc19..67708d23 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -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" diff --git a/ref/vk/vk_overlay.c b/ref/vk/vk_overlay.c index 83d524a9..33ef183c 100644 --- a/ref/vk/vk_overlay.c +++ b/ref/vk/vk_overlay.c @@ -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? } diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index eb26c410..0ba0b80a 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -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 diff --git a/ref/vk/vk_rmain.c b/ref/vk/vk_rmain.c index 52b21e28..9e295387 100644 --- a/ref/vk/vk_rmain.c +++ b/ref/vk/vk_rmain.c @@ -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; diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index 17fb633f..b756c4d3 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -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; diff --git a/ref/vk/vk_textures.c b/ref/vk/vk_textures.c index 073def47..89fb41bd 100644 --- a/ref/vk/vk_textures.c +++ b/ref/vk/vk_textures.c @@ -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 // 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; +} diff --git a/ref/vk/vk_textures.h b/ref/vk/vk_textures.h index 00b67154..72301255 100644 --- a/ref/vk/vk_textures.h +++ b/ref/vk/vk_textures.h @@ -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 );