diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index 1cfa4982d..6c6c33c31 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -1,8 +1,45 @@ +Longer-term agenda for current season: +- [ ] Better PBR math, e.g.: + - [ ] Black metals: https://github.com/w23/xash3d-fwgs/issues/666 + - [ ] Fresnel issues (esp. with skybox) + - [ ] Just make sure that all the BRDF math is correct +- [ ] Transparency/translucency: + - [ ] Proper material mode for translucency, with reflections, refraction (index), fresnel, etc. + - [ ] Figure out why additive transparency differs visibly from raster + - [ ] Extract and specialize effects, e.g. + - [ ] Rays -> volumetrics + - [ ] Glow -> bloom + - [ ] Smoke -> volumetrics + - [ ] Sprites/portals -> emissive volumetrics + - [ ] Holo models -> emissive additive + - [ ] Some additive -> translucent + - [ ] what else +- [ ] Render-graph-ish approach to resources. +- [ ] Performance tools -- needed for perf and lighting work below: + - [ ] Needs: render-graph-ish things for fast iterations when exporting custom situational metrics for the shader. + - [ ] Purpose: shader profiling. Measure impact of changes. Regressions. + - [ ] WIP shader clocks: https://github.com/w23/xash3d-fwgs/pull/692 + - [ ] WIP perf query: https://github.com/w23/xash3d-fwgs/pull/500 +- [ ] Lighting + - [ ] Point spheres sampling + - [ ] Increase limits + - [ ] s/poly/triangle/ -- simpler sampling, universal + - [ ] Better and dynamically sized clusters + - [ ] Cache rays -- do not cast shadow rays for everything, do a separate ray-only pass for visibility caching +- [ ] Bounces + - [ ] Moar bounces + - [ ] MIS + - [ ] Cache directions for strong indirect light + + +# 2023-12-19 E350 +- [x] fixup skybox reflections +- [x] improve logs "vk/tex: Loaded skybox pbr/env/%.*s" +- [x] add skybox test + # 2023-12-18 E349 - [x] KTX2 cubemaps -- [ ] improve logs "vk/tex: Loaded skybox pbr/env/%.*s" -- [ ] variable cubemap exposure -- [ ] add skybox test +- [x] variable cubemap exposure (in .mat file) # 2023-12-15 E348 - [x] fix ktx2 sides corruption @@ -38,32 +75,6 @@ - [x] Discuss shader profiling - [-] Discuss Env-based verbose log control -Longer-term agenda for current season: -- [ ] Tools: - - [ ] Shader profiling. Measure impact of changes. Regressions. -- [ ] Better PBR math, e.g.: -- [ ] Transparency: - - [ ] Figure out why additive transparency differs visibly from raster - - [ ] Extract and specialize effects, e.g. - - [ ] Rays -> volumetrics - - [ ] Glow -> bloom - - [ ] Smoke -> volumetrics - - [ ] Sprites/portals -> emissive volumetrics - - [ ] Holo models -> emissive additive - - [ ] Some additive -> translucent - - [ ] what else - - [ ] Proper material mode for translucency, with reflections, refraction (index), fresnel, etc. -- [ ] Lighting - - [ ] Point spheres sampling - - [ ] Increase limits - - [ ] s/poly/triangle/ -- simpler sampling, universal - - [ ] Better and dynamically sized clusters - - [ ] Cache rays -- do not cast shadow rays for everything, do a separate ray-only pass for visibility caching -- [ ] Bounces - - [ ] Moar bounces - - [ ] MIS - - [ ] Cache directions for strong indirect light - # 2023-12-04 E341 - [-] investigate envlight missing #680 - couldn't reproduce more than once diff --git a/ref/vk/camera.c b/ref/vk/camera.c index bf5273056..4fcba031d 100644 --- a/ref/vk/camera.c +++ b/ref/vk/camera.c @@ -158,3 +158,120 @@ int TriWorldToScreen( const float *world, float *screen ) return retval; } +// NOTE(nilsoncore): Moved from `vk_beams.c`. +// It uses global `g_camera` and comment suggests to use `R_SetupFrustum` -- a function inside `camera.c` -- so probably it is a good place. +#define RP_NORMALPASS() true // FIXME ??? +int CL_FxBlend( cl_entity_t *e ) // FIXME do R_SetupFrustum: , vec3_t vforward ) +{ + int blend = 0; + float offset, dist; + vec3_t tmp; + + offset = ((int)e->index ) * 363.0f; // Use ent index to de-sync these fx + + switch( e->curstate.renderfx ) + { + case kRenderFxPulseSlowWide: + blend = e->curstate.renderamt + 0x40 * sin( gpGlobals->time * 2 + offset ); + break; + case kRenderFxPulseFastWide: + blend = e->curstate.renderamt + 0x40 * sin( gpGlobals->time * 8 + offset ); + break; + case kRenderFxPulseSlow: + blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->time * 2 + offset ); + break; + case kRenderFxPulseFast: + blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->time * 8 + offset ); + break; + case kRenderFxFadeSlow: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt > 0 ) + e->curstate.renderamt -= 1; + else e->curstate.renderamt = 0; + } + blend = e->curstate.renderamt; + break; + case kRenderFxFadeFast: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt > 3 ) + e->curstate.renderamt -= 4; + else e->curstate.renderamt = 0; + } + blend = e->curstate.renderamt; + break; + case kRenderFxSolidSlow: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt < 255 ) + e->curstate.renderamt += 1; + else e->curstate.renderamt = 255; + } + blend = e->curstate.renderamt; + break; + case kRenderFxSolidFast: + if( RP_NORMALPASS( )) + { + if( e->curstate.renderamt < 252 ) + e->curstate.renderamt += 4; + else e->curstate.renderamt = 255; + } + blend = e->curstate.renderamt; + break; + case kRenderFxStrobeSlow: + blend = 20 * sin( gpGlobals->time * 4 + offset ); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxStrobeFast: + blend = 20 * sin( gpGlobals->time * 16 + offset ); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxStrobeFaster: + blend = 20 * sin( gpGlobals->time * 36 + offset ); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxFlickerSlow: + blend = 20 * (sin( gpGlobals->time * 2 ) + sin( gpGlobals->time * 17 + offset )); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxFlickerFast: + blend = 20 * (sin( gpGlobals->time * 16 ) + sin( gpGlobals->time * 23 + offset )); + if( blend < 0 ) blend = 0; + else blend = e->curstate.renderamt; + break; + case kRenderFxHologram: + case kRenderFxDistort: + VectorCopy( e->origin, tmp ); + VectorSubtract( tmp, g_camera.vieworg, tmp ); + dist = DotProduct( tmp, g_camera.vforward ); + + // turn off distance fade + if( e->curstate.renderfx == kRenderFxDistort ) + dist = 1; + + if( dist <= 0 ) + { + blend = 0; + } + else + { + e->curstate.renderamt = 180; + if( dist <= 100 ) blend = e->curstate.renderamt; + else blend = (int) ((1.0f - ( dist - 100 ) * ( 1.0f / 400.0f )) * e->curstate.renderamt ); + blend += gEngine.COM_RandomLong( -32, 31 ); + } + break; + default: + blend = e->curstate.renderamt; + break; + } + + blend = bound( 0, blend, 255 ); + + return blend; +} diff --git a/ref/vk/camera.h b/ref/vk/camera.h index 30f1ec0d8..f5516a6f5 100644 --- a/ref/vk/camera.h +++ b/ref/vk/camera.h @@ -28,5 +28,8 @@ void R_SetupCamera( const struct ref_viewpass_s *rvp ); int R_WorldToScreen( const vec3_t point, vec3_t screen ); int TriWorldToScreen( const float *world, float *screen ); +struct cl_entity_s; +int CL_FxBlend( struct cl_entity_s *e ); + // TODO move to infotool.h void XVK_CameraDebugPrintCenterEntity( void ); diff --git a/ref/vk/data/valve/luchiki/maps/c1a0.patch b/ref/vk/data/valve/luchiki/maps/c1a0.patch index d996a34aa..851203dff 100644 --- a/ref/vk/data/valve/luchiki/maps/c1a0.patch +++ b/ref/vk/data/valve/luchiki/maps/c1a0.patch @@ -75,10 +75,27 @@ { "_xvk_ent_id" "10 17 9 18 51 15 12 11 19 13 16" // remove hack lights } +// monitors +{ +"_xvk_surface_id" "930" +"_xvk_tex_offset" "-2.4 0" +} +{ +"_xvk_surface_id" "934" +"_xvk_tex_offset" "3 0" +} +{ +"_xvk_surface_id" "2082" +"_xvk_tex_offset" "-2.7 0" +} +{ +"_xvk_surface_id" "2086" +"_xvk_tex_offset" "-2.4 0" +} -// end +// end { "_xvk_ent_id" "20" // tune hack light entity "_cone" "0" diff --git a/ref/vk/data/valve/luchiki/maps/c1a1f.patch b/ref/vk/data/valve/luchiki/maps/c1a1f.patch index 5814bb2dd..5c9b4e50e 100644 --- a/ref/vk/data/valve/luchiki/maps/c1a1f.patch +++ b/ref/vk/data/valve/luchiki/maps/c1a1f.patch @@ -41,11 +41,29 @@ "origin" "-53 285 -142" } -// section 3 +// section 3 (servernaya) { "_xvk_ent_id" "283 296 275 278 291 273 292 271" // remove hack lights entity } +// monitors // TODO: BETTER STRETCH +{ +"_xvk_surface_id" "4146" // 4140 +"_xvk_tex_offset" "1 0" +} +{ +"_xvk_surface_id" "4158" // 4152 +"_xvk_tex_offset" "1.5 0" +} +{ +"_xvk_surface_id" "4170" // 4164 +"_xvk_tex_offset" "0.3 0" +} +{ +"_xvk_surface_id" "4176" // 4134 +"_xvk_tex_offset" "1.5 0" +} + { "_xvk_surface_id" "4592 4593 4594 4591 4589" // remove additive "god rays" fade2 "_xvk_material" "" @@ -159,14 +177,14 @@ } { "_xvk_surface_id" "796 795 807 800 809 808 805 801 796 780" -"_xvk_material" "generic_metal1" // TODO: texture -> material +"_xvk_material" "generic_metal1" } { "_xvk_smoothing_group" "766 765 772 773 777 776 768 769" } { "_xvk_surface_id" "766 765 772 773 777 776 768 769" -"_xvk_material" "generic_metal1" // TODO: texture -> material +"_xvk_material" "generic_metal1" } { diff --git a/ref/vk/data/valve/maps/c2a5a.rad b/ref/vk/data/valve/maps/c2a5a.rad new file mode 100644 index 000000000..ae08ab381 --- /dev/null +++ b/ref/vk/data/valve/maps/c2a5a.rad @@ -0,0 +1,4 @@ +//~LIGHT3C 220 210 150 3000 +~LIGHT3C 220 210 150 2000 +//+0~LIGHT4A 200 190 130 11000 ++0~LIGHT4A 200 190 130 5000 \ No newline at end of file diff --git a/ref/vk/data/valve/maps/c2a5d.rad b/ref/vk/data/valve/maps/c2a5d.rad new file mode 100644 index 000000000..2e6d3d6b3 --- /dev/null +++ b/ref/vk/data/valve/maps/c2a5d.rad @@ -0,0 +1,2 @@ ++0~FIFTIES_LGT2 175 175 255 5000 ++0~DRKMTLS2C 255 200 100 1000 \ No newline at end of file diff --git a/ref/vk/data/valve/maps/c2a5e.rad b/ref/vk/data/valve/maps/c2a5e.rad new file mode 100644 index 000000000..696967eee --- /dev/null +++ b/ref/vk/data/valve/maps/c2a5e.rad @@ -0,0 +1 @@ ++0~FIFTIES_LGT2 175 175 255 5000 diff --git a/ref/vk/r_textures.c b/ref/vk/r_textures.c index 7e6a469ee..9e01cc07f 100644 --- a/ref/vk/r_textures.c +++ b/ref/vk/r_textures.c @@ -22,6 +22,30 @@ #define MODULE_NAME "textures" #define LOG_MODULE tex +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +/* +static RVkModule *required_modules[] = { + // createDefaultTextures: R_VkTexturesSkyboxUpload + // loadTextureInternalFromFile: R_VkTextureUpload + // destroyTexture: R_VkTextureDestroy + // R_TextureUploadFromBuffer: R_VkTextureUpload + // skyboxLoadF: R_VkTexturesSkyboxUpload + // skyboxUnload: R_VkTexturesSkyboxUnload + // &g_module_textures // vk_textures -- @TexturesInitHardcode +}; +*/ + +RVkModule g_module_textures_api = { + .name = "textures_api", + .state = RVkModuleState_NotInitialized, + // .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .dependencies = RVkModuleDependencies_Empty, + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + vk_textures_global_t tglob = {0}; static struct { @@ -42,93 +66,6 @@ static void destroyTexture( uint texnum ); #define R_TextureUploadFromBufferNew(name, pic, flags) R_TextureUploadFromBuffer(name, pic, flags, /*update_only=*/false) -qboolean R_TexturesInit( void ) { - g_textures.mempool = Mem_AllocPool( "vktextures" ); - - g_textures.all_desc = (urmom_desc_t){ - .array = g_textures.all, - .count = COUNTOF(g_textures.all), - .item_size = sizeof(g_textures.all[0]), - .type = kUrmomStringInsensitive, - }; - - urmomInit(&g_textures.all_desc); - - // Mark index 0 as occupied to have a special "no texture" value - g_textures.all[0].hdr_.hash = 0x7fffffff; - g_textures.all[0].hdr_.state = 1; - Q_strncpy( g_textures.all[0].hdr_.key, "*unused*", sizeof(g_textures.all[0].hdr_.key)); - - createDefaultTextures(); - - if (!R_VkTexturesInit()) - return false; - - return true; -} - -void R_TexturesShutdown( void ) -{ - destroyDefaultTextures(); - - // By this point ideally all texture should have been destroyed. - // However, there are two possible ways some texture could have been left over: - // 1. Our coding mistakes, not releasing textures when done - // 2. Engine and other external things not cleaning up (e.g. mainui is known to leave textures) - for( int i = 1; i < COUNTOF(g_textures.all); i++ ) { - const vk_texture_t *const tex = g_textures.all + i; - if (!URMOM_IS_OCCUPIED(tex->hdr_)) - continue; - - // Try to free external textures - R_TextureFree( i ); - - // If it is still not deleted, complain loudly - if (URMOM_IS_OCCUPIED(tex->hdr_)) { - // TODO consider ASSERT, as this is a coding mistake - ERR("stale texture[%d] '%s' refcount=%d", i, TEX_NAME(tex), tex->refcount); - destroyTexture( i ); - } - } - - int is_deleted_count = 0; - int clusters[16] = {0}; - int current_cluster_begin = -1; - for( int i = 1; i < COUNTOF(g_textures.all); i++ ) { - const vk_texture_t *const tex = g_textures.all + i; - - if (URMOM_IS_EMPTY(tex->hdr_)) { - if (current_cluster_begin >= 0) { - const int cluster_length = i - current_cluster_begin; - clusters[cluster_length >= COUNTOF(clusters) ? 0 : cluster_length]++; - } - current_cluster_begin = -1; - } else { - if (current_cluster_begin < 0) - current_cluster_begin = i; - } - - if (URMOM_IS_DELETED(tex->hdr_)) - ++is_deleted_count; - - ASSERT(!URMOM_IS_OCCUPIED(tex->hdr_)); - } - - // TODO handle wraparound clusters - if (current_cluster_begin >= 0) { - const int cluster_length = COUNTOF(g_textures.all) - current_cluster_begin; - clusters[cluster_length >= COUNTOF(clusters) ? 0 : cluster_length]++; - } - - DEBUG("Deleted slots in texture hash table: %d", is_deleted_count); - for (int i = 1; i < COUNTOF(clusters); ++i) - DEBUG("Texture hash table cluster[%d] = %d", i, clusters[i]); - - DEBUG("Clusters longer than %d: %d", (int)COUNTOF(clusters)-1, clusters[0]); - - R_VkTexturesShutdown(); -} - static qboolean checkTextureName( const char *name ) { int len; @@ -767,17 +704,21 @@ static void skyboxParseInfo( const char *name ) { Mem_Free(data); } -static qboolean skyboxLoad(const_string_view_t base, const char *prefix) { +static qboolean skyboxLoadF(const char *fmt, ...) { qboolean success = false; - char loadname[MAX_STRING]; - Q_snprintf( loadname, sizeof( loadname ), prefix, base.len, base.s ); + char buffer[MAX_STRING]; + + va_list argptr; + va_start( argptr, fmt ); + Q_vsnprintf( buffer, sizeof buffer, fmt, argptr ); + va_end( argptr ); - rgbdata_t *pic = gEngine.FS_LoadImage( loadname, NULL, 0 ); + rgbdata_t *pic = gEngine.FS_LoadImage( buffer, NULL, 0 ); if (!pic) return false; if (!(pic->flags & IMAGE_CUBEMAP)) { - ERR("%s: '%s' is invalid: skybox is expected to be a cubemap ", __FUNCTION__, loadname); + ERR("%s: '%s' is invalid: skybox is expected to be a cubemap ", __FUNCTION__, buffer); goto cleanup; } @@ -791,18 +732,18 @@ static qboolean skyboxLoad(const_string_view_t base, const char *prefix) { { const qboolean is_placeholder = false; - success = R_VkTexturesSkyboxUpload( prefix, pic, kColorspaceGamma, is_placeholder ); + success = R_VkTexturesSkyboxUpload( buffer, pic, kColorspaceGamma, is_placeholder ); } if (success) - skyboxParseInfo(loadname); + skyboxParseInfo(buffer); cleanup: if (pic) gEngine.FS_FreeImage(pic); if (success) - DEBUG( "Loaded skybox %s", prefix ); + DEBUG( "Loaded skybox %s", buffer ); return success; } @@ -841,11 +782,11 @@ static qboolean skyboxTryLoad( const char *skyboxname, qboolean force_reload ) { return true; // Try loading newer "PBR" upscaled skybox first - if (skyboxLoad(basename, "pbr/env/%.*s")) + if (skyboxLoadF("pbr/env/%.*s", basename.len, basename.s)) goto success; // Try loading original game skybox - if (skyboxLoad(basename, "gfx/env/%.*s")) + if (skyboxLoadF("gfx/env/%.*s", basename.len, basename.s)) goto success; skyboxUnload(); @@ -982,3 +923,105 @@ void R_TextureRelease( unsigned int texnum ) { const qboolean ref_interface = false; releaseTexture( texnum, ref_interface ); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_textures_api ); + + g_textures.mempool = Mem_AllocPool( "vktextures" ); + + g_textures.all_desc = (urmom_desc_t){ + .array = g_textures.all, + .count = COUNTOF(g_textures.all), + .item_size = sizeof(g_textures.all[0]), + .type = kUrmomStringInsensitive, + }; + + urmomInit(&g_textures.all_desc); + + // Mark index 0 as occupied to have a special "no texture" value + g_textures.all[0].hdr_.hash = 0x7fffffff; + g_textures.all[0].hdr_.state = 1; + Q_strncpy( g_textures.all[0].hdr_.key, "*unused*", sizeof(g_textures.all[0].hdr_.key)); + + createDefaultTextures(); + + // NOTE(nilsoncore): This has to be hardcoded in a current approach. -- @TexturesInitHardcode + // They use each other's functions, so it will be a circular dependency if we include them in a normal way. + // We could instead not turn `textures` (vk_textures{h,c}) into a whole module and leave it as it is, + // but it has `Init` and `Shutdown` functions like in a normal module, so it's bad either way. + // This probably needs some reorganization / change of logic. + g_module_textures.init_caller = &g_module_textures_api; + if ( !g_module_textures.Init() ) { + ERR( "Couldn't initialize '%s' submodule.", g_module_textures.name ); + g_module_textures_api.state = RVkModuleState_NotInitialized; + return false; + } + + XRVkModule_OnInitEnd( g_module_textures_api ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_textures_api ); + + destroyDefaultTextures(); + + // By this point ideally all texture should have been destroyed. + // However, there are two possible ways some texture could have been left over: + // 1. Our coding mistakes, not releasing textures when done + // 2. Engine and other external things not cleaning up (e.g. mainui is known to leave textures) + for( int i = 1; i < COUNTOF(g_textures.all); i++ ) { + const vk_texture_t *const tex = g_textures.all + i; + if (!URMOM_IS_OCCUPIED(tex->hdr_)) + continue; + + // Try to free external textures + R_TextureFree( i ); + + // If it is still not deleted, complain loudly + if (URMOM_IS_OCCUPIED(tex->hdr_)) { + // TODO consider ASSERT, as this is a coding mistake + ERR("stale texture[%d] '%s' refcount=%d", i, TEX_NAME(tex), tex->refcount); + destroyTexture( i ); + } + } + + int is_deleted_count = 0; + int clusters[16] = {0}; + int current_cluster_begin = -1; + for( int i = 1; i < COUNTOF(g_textures.all); i++ ) { + const vk_texture_t *const tex = g_textures.all + i; + + if (URMOM_IS_EMPTY(tex->hdr_)) { + if (current_cluster_begin >= 0) { + const int cluster_length = i - current_cluster_begin; + clusters[cluster_length >= COUNTOF(clusters) ? 0 : cluster_length]++; + } + current_cluster_begin = -1; + } else { + if (current_cluster_begin < 0) + current_cluster_begin = i; + } + + if (URMOM_IS_DELETED(tex->hdr_)) + ++is_deleted_count; + + ASSERT(!URMOM_IS_OCCUPIED(tex->hdr_)); + } + + // TODO handle wraparound clusters + if (current_cluster_begin >= 0) { + const int cluster_length = COUNTOF(g_textures.all) - current_cluster_begin; + clusters[cluster_length >= COUNTOF(clusters) ? 0 : cluster_length]++; + } + + DEBUG("Deleted slots in texture hash table: %d", is_deleted_count); + for (int i = 1; i < COUNTOF(clusters); ++i) + DEBUG("Texture hash table cluster[%d] = %d", i, clusters[i]); + + DEBUG("Clusters longer than %d: %d", (int)COUNTOF(clusters)-1, clusters[0]); + + g_module_textures.Shutdown(); // -- @TexturesInitHardcode + + XRVkModule_OnShutdownEnd( g_module_textures_api ); +} diff --git a/ref/vk/r_textures.h b/ref/vk/r_textures.h index f8c03e358..08a945a5d 100644 --- a/ref/vk/r_textures.h +++ b/ref/vk/r_textures.h @@ -1,5 +1,7 @@ #pragma once +#include "vk_module.h" + #include "const.h" // required for com_model.h, ref_api.h #include "cvardef.h" // required for ref_api.h #include "com_model.h" // required for ref_api.h @@ -7,6 +9,8 @@ #define MAX_LIGHTMAPS 256 +extern RVkModule g_module_textures_api; + typedef struct vk_textures_global_s { // TODO Fix these at compile time statically, akin to BLUE_NOISE_TEXTURE_ID @@ -30,9 +34,6 @@ typedef struct vk_textures_global_s // 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... ? diff --git a/ref/vk/shaders/bounce.comp b/ref/vk/shaders/bounce.comp index a145f5a43..7cbf9118b 100644 --- a/ref/vk/shaders/bounce.comp +++ b/ref/vk/shaders/bounce.comp @@ -158,17 +158,16 @@ void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specula payload.base_color_a = vec4(0.); payload.emissive = vec4(0.); payload.material_rmxx = vec4(0.); - const vec3 pos = pos_t.xyz + geometry_normal * ray_normal_fudge; - if (!getHit(pos, bounce_direction, payload)) - return; - // 4. Sample light sources - { - vec3 ldiffuse = vec3(0.); - vec3 lspecular = vec3(0.); + vec3 lighting = vec3(0.); + const vec3 pos = pos_t.xyz + geometry_normal * ray_normal_fudge; + if (getHit(pos, bounce_direction, payload)) { + // 4. Sample light sources const vec3 hit_pos = payload.hit_t.xyz; const vec3 hit_shading_normal = normalDecode(payload.normals_gs.zw); + vec3 ldiffuse = vec3(0.); + vec3 lspecular = vec3(0.); MaterialProperties hit_material; hit_material.baseColor = vec3(1.); hit_material.emissive = vec3(0.f); @@ -181,13 +180,16 @@ void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specula vec3 emissive = vec3(0.); traceSimpleBlending(pos, bounce_direction, payload.hit_t.w, emissive, background); - const vec3 final_color = emissive + background; - - if (brdf_type == DIFFUSE_TYPE) - diffuse += final_color; - else - specular += final_color + payload.emissive.rgb; + lighting = emissive + background; + } else { + lighting = texture(skybox, bounce_direction).rgb * ubo.ubo.skybox_exposure; + //payload.emissive.rgb = texture(skybox, bounce_direction).rgb * ubo.ubo.skybox_exposure; } + + if (brdf_type == DIFFUSE_TYPE) + diffuse += lighting; + else + specular += lighting + payload.emissive.rgb; } diff --git a/ref/vk/vk_beams.c b/ref/vk/vk_beams.c index 735fd4b21..69b7208f5 100644 --- a/ref/vk/vk_beams.c +++ b/ref/vk/vk_beams.c @@ -18,6 +18,42 @@ #define NOISE_DIVISIONS 64 // don't touch - many tripmines cause the crash when it equal 128 #define MODULE_NAME "beams" +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // TriBrightness: TriColor4f + // R_DrawSegs: TriBegin, TriTexCoord2f, TriNormal3fv, TriVertex3fv, TriEndEx + // R_DrawTorus: TriBegin, TriTexCoord2f, TriVertex3fv, TriEndEx + // R_DrawDisk: TriBegin, TriTexCoord2f, TriVertex3fv, TriEndEx + // R_DrawCylinder: TriBegin, TriTexCoord2f, TriVertex3fv, TriEndEx + // R_DrawBeamFollow: TriBegin, TriTexCoord2f, TriVertex3fv, TriEndEx + // R_DrawRing: TriBegin, TriTexCoord2f, TriVertex3fv, TriEndEx + // R_BeamDraw: TriSetTexture, TriRenderMode, TriColor4f + &g_module_triapi, + + // R_BeamDraw: R_GetSpriteTexture + &g_module_sprite, + + // R_BeamDraw: VK_RenderDebugLabelBegin, VK_RenderDebugLabelEnd + &g_module_render + + // camera + // R_DrawTorus: TriWorldToScreen + // R_DrawBeamFollow: TriWorldToScreen + // R_DrawRing: TriWorldToScreen + // R_BeamDraw: CL_FxBlend + // R_BeamDrawCustomEntity: CL_FxBlend +}; + +RVkModule g_module_beams = { + .name = "beams", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { vec3_t pos; @@ -31,11 +67,6 @@ static struct { } stats; } g_beam; -qboolean R_BeamInit(void) { - R_SPEEDS_COUNTER(g_beam.stats.beams, "count", kSpeedsMetricCount); - return true; -} - /* ============================================================== @@ -1223,3 +1254,19 @@ void R_BeamDrawCustomEntity( cl_entity_t *ent, float frametime ) R_BeamDraw( &beam, frametime ); } +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_beams ); + + R_SPEEDS_COUNTER(g_beam.stats.beams, "count", kSpeedsMetricCount); + + XRVkModule_OnInitEnd( g_module_beams ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_beams ); + + // Nothing to clear for now. + + XRVkModule_OnShutdownEnd( g_module_beams ); +} diff --git a/ref/vk/vk_beams.h b/ref/vk/vk_beams.h index 941a6588a..d50564184 100644 --- a/ref/vk/vk_beams.h +++ b/ref/vk/vk_beams.h @@ -1,7 +1,10 @@ #pragma once +#include "vk_module.h" #include "xash3d_types.h" +extern RVkModule g_module_beams; + struct cl_entity_s; struct beam_s; @@ -9,5 +12,3 @@ qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ); void R_BeamDrawCustomEntity( struct cl_entity_s *ent, float frametime ); void R_BeamDraw( struct beam_s *pbeam, float frametime ); - -qboolean R_BeamInit(void); diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 83d4ed3bb..b775ceae1 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -28,6 +28,60 @@ #define MODULE_NAME "brush" #define LOG_MODULE brush +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // brushComputeWaterPolys: R_VkMaterialGetForTexture + // R_BrushModelDraw: R_VkMaterialGetForTexture + // fillBrushSurfaces: R_VkMaterialGetForRef, R_VkMaterialGetEx, R_VkMaterialGetForTexture + &g_module_materials, + + // brushComputeWaterPolys: RT_GetEmissiveForTexture + // R_BrushModelDraw: RT_LightAddPolygon, RT_GetEmissiveForTexture, T_LightAddPolygon + // R_VkBrushModelCollectEmissiveSurfaces: RT_GetEmissiveForTexture, RT_LightAddPolygon + &g_module_light, + + // fillWaterSurfaces: R_GeometryRangeLock, R_GeometryRangeUnlock + // brushCreateWaterModel: R_GeometryRangeAlloc + // R_BrushModelDraw: R_GeometryRangeLockSubrange, R_GeometryRangeUnlock + // createRenderModel: R_GeometryRangeAlloc, R_GeometryRangeLock, R_GeometryRangeUnlock + // R_BrushModelDestroy: R_GeometryRangeFree + &g_module_geometry, + + // brushCreateWaterModel: R_RenderModelCreate + // brushDrawWater: R_RenderModelUpdate, R_RenderModelDraw + // R_BrushModelDraw: R_RenderModelUpdateMaterials, R_RenderModelDraw + // createRenderModel: R_RenderModelCreate + // R_BrushModelDestroy: R_RenderModelDestroy + // R_BrushModelDestroyAll: R_BrushModelDestroy + // R_VkBrushModelCollectEmissiveSurfaces: R_RenderModelUpdateMaterials + &g_module_render, + + // fillBrushSurfaces: R_TexturesGetParm + // R_BrushUnloadTextures: R_TextureFree + &g_module_textures_api, + + // R_VkBrushModelCollectEmissiveSurfaces: R_VkStagingFlushSync + &g_module_staging + + // mapents + // getSurfaceType: R_VkPatchGetSurface + // fillBrushSurfaces: R_VkPatchGetSurface + // R_VkBrushModelCollectEmissiveSurfaces: R_VkPatchGetSurface + + // lightmap + // fillBrushSurfaces: VK_CreateSurfaceLightmap +}; + +RVkModule g_module_brush = { + .name = "brush", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { int surfaces_count; const int *surfaces_indices; @@ -151,22 +205,6 @@ void VK_InitRandomTable( void ) gEngine.COM_SetRandomSeed( 0 ); } -qboolean R_BrushInit( void ) -{ - VK_InitRandomTable (); - - R_SPEEDS_COUNTER(g_brush.stat.models_drawn, "drawn", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_brush.stat.water_surfaces_drawn, "water.surfaces", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_brush.stat.water_polys_drawn, "water.polys", kSpeedsMetricCount); - - return true; -} - -void R_BrushShutdown( void ) { - if (g_brush.conn.edges) - Mem_Free(g_brush.conn.edges); -} - // speed up sin calculations static const float r_turbsin[] = { @@ -1909,3 +1947,25 @@ void R_BrushUnloadTextures( model_t *mod ) R_TextureFree( tx->fb_texturenum ); // luma texture } } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_brush ); + + VK_InitRandomTable(); + + R_SPEEDS_COUNTER(g_brush.stat.models_drawn, "drawn", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_brush.stat.water_surfaces_drawn, "water.surfaces", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_brush.stat.water_polys_drawn, "water.polys", kSpeedsMetricCount); + + XRVkModule_OnInitEnd( g_module_brush ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_brush ); + + if (g_brush.conn.edges) + Mem_Free(g_brush.conn.edges); + + XRVkModule_OnShutdownEnd( g_module_brush ); +} diff --git a/ref/vk/vk_brush.h b/ref/vk/vk_brush.h index 66c44afd7..b47fc3712 100644 --- a/ref/vk/vk_brush.h +++ b/ref/vk/vk_brush.h @@ -1,16 +1,17 @@ #pragma once +#include "vk_module.h" + #include "xash3d_types.h" #include "vk_render.h" // cl_entity_t +extern RVkModule g_module_brush; + struct ref_viewpass_s; struct draw_list_s; struct model_s; struct cl_entity_s; -qboolean R_BrushInit( void ); -void R_BrushShutdown( void ); - qboolean R_BrushModelLoad(struct model_s *mod, qboolean is_worldmodel); void R_BrushModelDestroyAll( void ); diff --git a/ref/vk/vk_buffer.c b/ref/vk/vk_buffer.c index ac4dfc72f..0ac48094c 100644 --- a/ref/vk/vk_buffer.c +++ b/ref/vk/vk_buffer.c @@ -1,5 +1,22 @@ #include "vk_buffer.h" +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // VK_BufferCreate: VK_DevMemAllocate + // VK_BufferDestroy: VK_DevMemFree + &g_module_devmem +}; + +RVkModule g_module_buffer = { + .name = "buffer", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + qboolean VK_BufferCreate(const char *debug_name, vk_buffer_t *buf, uint32_t size, VkBufferUsageFlags usage, VkMemoryPropertyFlags flags) { VkBufferCreateInfo bci = { @@ -17,6 +34,8 @@ qboolean VK_BufferCreate(const char *debug_name, vk_buffer_t *buf, uint32_t size if (usage & VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR) { memreq.alignment = ALIGN_UP(memreq.alignment, vk_core.physical_device.properties_ray_tracing_pipeline.shaderGroupBaseAlignment); } + + ASSERT( g_module_devmem.state == RVkModuleState_Initialized && "VK_BufferCreate: 'devmem' is not initialized." ); buf->devmem = VK_DevMemAllocate(debug_name, memreq, flags, usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT ? VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT : 0); XVK_CHECK(vkBindBufferMemory(vk_core.device, buf->buffer, buf->devmem.device_memory, buf->devmem.offset)); @@ -109,3 +128,20 @@ uint32_t R_DEBuffer_Alloc(r_debuffer_t* debuf, r_lifetime_t lifetime, uint32_t s void R_DEBuffer_Flip(r_debuffer_t* debuf) { R_FlippingBuffer_Flip(&debuf->dynamic); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_buffer ); + + // Nothing to init for now. + + XRVkModule_OnInitEnd( g_module_buffer ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_buffer ); + + // Nothing to clear for now. + + XRVkModule_OnShutdownEnd( g_module_buffer ); +} diff --git a/ref/vk/vk_buffer.h b/ref/vk/vk_buffer.h index 5e977fe02..d6d0217be 100644 --- a/ref/vk/vk_buffer.h +++ b/ref/vk/vk_buffer.h @@ -1,10 +1,14 @@ #pragma once #include "vk_core.h" +#include "vk_module.h" + #include "vk_devmem.h" #include "r_flipping.h" #include "alolcator.h" +extern RVkModule g_module_buffer; + typedef struct vk_buffer_s { vk_devmem_t devmem; VkBuffer buffer; diff --git a/ref/vk/vk_combuf.c b/ref/vk/vk_combuf.c index ba7339a03..f95b57992 100644 --- a/ref/vk/vk_combuf.c +++ b/ref/vk/vk_combuf.c @@ -1,5 +1,8 @@ #include "vk_combuf.h" +#include "vk_module.h" + #include "vk_commandpool.h" +#include "vk_logs.h" #include "profiler.h" @@ -8,6 +11,17 @@ #define BEGIN_INDEX_TAG 0x10000000 +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +RVkModule g_module_combuf = { + .name = "combuf", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_Empty, + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { vk_combuf_t public; int used; @@ -33,44 +47,6 @@ static struct { int entire_combuf_scope_id; } g_combuf; -qboolean R_VkCombuf_Init( void ) { - g_combuf.pool = R_VkCommandPoolCreate(MAX_COMMANDBUFFERS); - if (!g_combuf.pool.pool) - return false; - - const VkQueryPoolCreateInfo qpci = { - .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, - .pNext = NULL, - .queryType = VK_QUERY_TYPE_TIMESTAMP, - .queryCount = COUNTOF(g_combuf.timestamp.values), - .flags = 0, - }; - - XVK_CHECK(vkCreateQueryPool(vk_core.device, &qpci, NULL, &g_combuf.timestamp.pool)); - - for (int i = 0; i < MAX_COMMANDBUFFERS; ++i) { - vk_combuf_impl_t *const cb = g_combuf.combufs + i; - - cb->public.cmdbuf = g_combuf.pool.buffers[i]; - SET_DEBUG_NAMEF(cb->public.cmdbuf, VK_OBJECT_TYPE_COMMAND_BUFFER, "cmdbuf[%d]", i); - - cb->profiler.timestamps_offset = i * MAX_QUERY_COUNT; - } - - g_combuf.entire_combuf_scope_id = R_VkGpuScope_Register("GPU"); - - return true; -} - -void R_VkCombuf_Destroy( void ) { - vkDestroyQueryPool(vk_core.device, g_combuf.timestamp.pool, NULL); - R_VkCommandPoolDestroy(&g_combuf.pool); - - for (int i = 0; i < g_combuf.scopes_count; ++i) { - Mem_Free((char*)g_combuf.scopes[i].name); - } -} - vk_combuf_t* R_VkCombufOpen( void ) { for (int i = 0; i < MAX_COMMANDBUFFERS; ++i) { vk_combuf_impl_t *const cb = g_combuf.combufs + i; @@ -230,3 +206,51 @@ vk_combuf_scopes_t R_VkCombufScopesGet( vk_combuf_t *pub ) { .entries_count = cb->profiler.scopes_count, }; } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_combuf ); + + g_combuf.pool = R_VkCommandPoolCreate(MAX_COMMANDBUFFERS); + if (!g_combuf.pool.pool) { + ERR( "Couldn't create Vulkan command pool." ); + g_module_combuf.state = RVkModuleState_NotInitialized; + return false; + } + + const VkQueryPoolCreateInfo qpci = { + .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, + .pNext = NULL, + .queryType = VK_QUERY_TYPE_TIMESTAMP, + .queryCount = COUNTOF(g_combuf.timestamp.values), + .flags = 0, + }; + + XVK_CHECK(vkCreateQueryPool(vk_core.device, &qpci, NULL, &g_combuf.timestamp.pool)); + + for (int i = 0; i < MAX_COMMANDBUFFERS; ++i) { + vk_combuf_impl_t *const cb = g_combuf.combufs + i; + + cb->public.cmdbuf = g_combuf.pool.buffers[i]; + SET_DEBUG_NAMEF(cb->public.cmdbuf, VK_OBJECT_TYPE_COMMAND_BUFFER, "cmdbuf[%d]", i); + + cb->profiler.timestamps_offset = i * MAX_QUERY_COUNT; + } + + g_combuf.entire_combuf_scope_id = R_VkGpuScope_Register("GPU"); + + XRVkModule_OnInitEnd( g_module_combuf ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_combuf ); + + vkDestroyQueryPool(vk_core.device, g_combuf.timestamp.pool, NULL); + R_VkCommandPoolDestroy(&g_combuf.pool); + + for (int i = 0; i < g_combuf.scopes_count; ++i) { + Mem_Free((char*)g_combuf.scopes[i].name); + } + + XRVkModule_OnShutdownEnd( g_module_combuf ); +} diff --git a/ref/vk/vk_combuf.h b/ref/vk/vk_combuf.h index 783e49d71..7c94d943e 100644 --- a/ref/vk/vk_combuf.h +++ b/ref/vk/vk_combuf.h @@ -4,13 +4,12 @@ #define MAX_GPU_SCOPES 64 +extern RVkModule g_module_combuf; + typedef struct vk_combuf_s { VkCommandBuffer cmdbuf; } vk_combuf_t; -qboolean R_VkCombuf_Init( void ); -void R_VkCombuf_Destroy( void ); - vk_combuf_t* R_VkCombufOpen( void ); void R_VkCombufClose( vk_combuf_t* ); diff --git a/ref/vk/vk_core.c b/ref/vk/vk_core.c index a7c3e334e..0aff02643 100644 --- a/ref/vk/vk_core.c +++ b/ref/vk/vk_core.c @@ -2,6 +2,7 @@ #include "vk_common.h" #include "r_textures.h" +#include "vk_textures.h" #include "vk_overlay.h" #include "vk_renderstate.h" #include "vk_staging.h" @@ -677,35 +678,6 @@ 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 @@ -762,70 +734,67 @@ qboolean R_VkInit( void ) return false; } -#if USE_AFTERMATH - if (!VK_AftermathInit()) { - gEngine.Con_Printf( S_ERROR "Cannot initialize Nvidia Nsight Aftermath SDK\n" ); - } -#endif - if (!createDevice()) return false; VK_LoadCvarsAfterInit(); - if (!R_VkCombuf_Init()) - return false; - if (!initSurface()) return false; - if (!VK_DevMemInit()) - return false; - - if (!R_VkStagingInit()) - return false; - - if (!VK_PipelineInit()) - return false; - - // TODO ... - if (!VK_DescriptorInit()) - return false; - - if (!VK_FrameCtlInit()) - return false; - - if (!R_GeometryBuffer_Init()) - return false; - - if (!VK_RenderInit()) - return false; - - VK_StudioInit(); - - VK_SceneInit(); - - R_TexturesInit(); - - // All below need render_pass - - if (!R_VkOverlay_Init()) - return false; - - if (!R_BrushInit()) - return false; - - if (vk_core.rtx) - { - if (!VK_RayInit()) - return false; - - // FIXME move all this to rt-specific modules - VK_LightsInit(); + /* Print all modules with their dependencies (not printing states) */ + + RVkModule_PrintDependencyList( &g_module_combuf, false ); + RVkModule_PrintDependencyList( &g_module_buffer, false ); + RVkModule_PrintDependencyList( &g_module_devmem, false ); + RVkModule_PrintDependencyList( &g_module_nv_aftermath, false ); + RVkModule_PrintDependencyList( &g_module_staging, false ); + RVkModule_PrintDependencyList( &g_module_image, false ); + RVkModule_PrintDependencyList( &g_module_pipeline, false ); + RVkModule_PrintDependencyList( &g_module_descriptor, false ); + RVkModule_PrintDependencyList( &g_module_framectl, false ); + RVkModule_PrintDependencyList( &g_module_geometry, false ); + RVkModule_PrintDependencyList( &g_module_render, false ); + RVkModule_PrintDependencyList( &g_module_studio, false ); + RVkModule_PrintDependencyList( &g_module_scene, false ); + RVkModule_PrintDependencyList( &g_module_textures_api, false ); + RVkModule_PrintDependencyList( &g_module_textures, false ); + RVkModule_PrintDependencyList( &g_module_overlay, false ); + RVkModule_PrintDependencyList( &g_module_brush, false ); + RVkModule_PrintDependencyList( &g_module_rtx, false ); + RVkModule_PrintDependencyList( &g_module_light, false ); + RVkModule_PrintDependencyList( &g_module_sprite, false ); + RVkModule_PrintDependencyList( &g_module_beams, false ); + + /* Modules without dependencies */ + + if ( !g_module_combuf.Init() ) return false; + if ( !g_module_devmem.Init() ) return false; + if ( !g_module_descriptor.Init() ) return false; + if ( !g_module_nv_aftermath.Init() ) return false; + + /* Modules with dependencies */ + + if ( !g_module_buffer.Init() ) return false; + if ( !g_module_staging.Init() ) return false; + if ( !g_module_image.Init() ) return false; + if ( !g_module_pipeline.Init() ) return false; + if ( !g_module_framectl.Init() ) return false; + if ( !g_module_geometry.Init() ) return false; + if ( !g_module_render.Init() ) return false; + if ( !g_module_studio.Init() ) return false; + if ( !g_module_scene.Init() ) return false; + if ( !g_module_textures_api.Init() ) return false; // +g_module_textures (hardcoded) -- @TexturesInitHardcode + if ( !g_module_overlay.Init() ) return false; + if ( !g_module_brush.Init() ) return false; + + if ( vk_core.rtx ) { + if ( !g_module_rtx.Init() ) return false; + if ( !g_module_light.Init() ) return false; } - R_SpriteInit(); - R_BeamInit(); + if ( !g_module_sprite.Init() ) return false; + if ( !g_module_beams.Init() ) return false; return true; } @@ -835,43 +804,34 @@ void R_VkShutdown( void ) { VK_EntityDataClear(); - R_SpriteShutdown(); - - if (vk_core.rtx) - { - VK_LightsShutdown(); - VK_RayShutdown(); + g_module_beams.Shutdown(); + g_module_sprite.Shutdown(); + + if ( vk_core.rtx ) { + g_module_light.Shutdown(); + g_module_rtx.Shutdown(); } - R_BrushShutdown(); - VK_StudioShutdown(); - R_VkOverlay_Shutdown(); - - VK_RenderShutdown(); - R_GeometryBuffer_Shutdown(); - - VK_FrameCtlShutdown(); - - R_VkMaterialsShutdown(); - - R_TexturesShutdown(); - - VK_PipelineShutdown(); - - VK_DescriptorShutdown(); - - R_VkStagingShutdown(); - - R_VkCombuf_Destroy(); - - VK_DevMemDestroy(); + g_module_brush.Shutdown(); + g_module_overlay.Shutdown(); + g_module_textures_api.Shutdown(); + g_module_textures.Shutdown(); + g_module_scene.Shutdown(); + g_module_studio.Shutdown(); + g_module_render.Shutdown(); + g_module_geometry.Shutdown(); + g_module_framectl.Shutdown(); + g_module_pipeline.Shutdown(); + g_module_staging.Shutdown(); + g_module_buffer.Shutdown(); + + g_module_nv_aftermath.Shutdown(); + g_module_descriptor.Shutdown(); + g_module_devmem.Shutdown(); + g_module_combuf.Shutdown(); vkDestroyDevice(vk_core.device, NULL); -#if USE_AFTERMATH - VK_AftermathShutdown(); -#endif - if (vk_core.debug_messenger) { vkDestroyDebugUtilsMessengerEXT(vk_core.instance, vk_core.debug_messenger, NULL); diff --git a/ref/vk/vk_descriptor.c b/ref/vk/vk_descriptor.c index 05ede5aaa..1eb4c3770 100644 --- a/ref/vk/vk_descriptor.c +++ b/ref/vk/vk_descriptor.c @@ -2,110 +2,18 @@ #include "eiface.h" // ARRAYSIZE -descriptor_pool_t vk_desc_fixme; - -qboolean VK_DescriptorInit( void ) -{ - int max_desc_sets = 0; - - VkDescriptorPoolSize dps[] = { - { - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = MAX_TEXTURES, - }, { - .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - .descriptorCount = ARRAYSIZE(vk_desc_fixme.ubo_sets), - /* - }, { - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = 1, - */ - }, - }; - VkDescriptorPoolCreateInfo dpci = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .pPoolSizes = dps, - .poolSizeCount = ARRAYSIZE(dps), - }; +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); - for (int i = 0; i < ARRAYSIZE(dps); ++i) - max_desc_sets += dps[i].descriptorCount; +RVkModule g_module_descriptor = { + .name = "descriptor", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_Empty, + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; - dpci.maxSets = max_desc_sets; - - XVK_CHECK(vkCreateDescriptorPool(vk_core.device, &dpci, NULL, &vk_desc_fixme.pool)); - - { - const int num_sets = MAX_TEXTURES; - // ... TODO find better place for this; this should be per-pipeline/shader - const VkDescriptorSetLayoutBinding bindings[] = { { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = NULL, - }}; - 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); - const VkDescriptorSetAllocateInfo dsai = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = vk_desc_fixme.pool, - .descriptorSetCount = num_sets, - .pSetLayouts = tmp_layouts, - }; - XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc_fixme.one_texture_layout)); - for (int i = 0; i < num_sets; ++i) - tmp_layouts[i] = vk_desc_fixme.one_texture_layout; - - XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc_fixme.texture_sets)); - - Mem_Free(tmp_layouts); - } - - { - const int num_sets = ARRAYSIZE(vk_desc_fixme.ubo_sets); - // ... TODO find better place for this; this should be per-pipeline/shader - VkDescriptorSetLayoutBinding bindings[] = { { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - .descriptorCount = 1, - // TODO we use these sets for both vertex-only and fragment-only bindings; improve - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - }}; - 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 = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = vk_desc_fixme.pool, - .descriptorSetCount = num_sets, - .pSetLayouts = tmp_layouts, - }; - XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc_fixme.one_uniform_buffer_layout)); - for (int i = 0; i < num_sets; ++i) - tmp_layouts[i] = vk_desc_fixme.one_uniform_buffer_layout; - - XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc_fixme.ubo_sets)); - - Mem_Free(tmp_layouts); - } - - return true; -} - -void VK_DescriptorShutdown( void ) -{ - vkDestroyDescriptorPool(vk_core.device, vk_desc_fixme.pool, NULL); - vkDestroyDescriptorSetLayout(vk_core.device, vk_desc_fixme.one_texture_layout, NULL); - vkDestroyDescriptorSetLayout(vk_core.device, vk_desc_fixme.one_uniform_buffer_layout, NULL); -} +descriptor_pool_t vk_desc_fixme; void VK_DescriptorsCreate(vk_descriptors_t *desc) { @@ -220,3 +128,111 @@ void VK_DescriptorsDestroy(const vk_descriptors_t *desc) vkDestroyPipelineLayout(vk_core.device, desc->pipeline_layout, NULL); vkDestroyDescriptorSetLayout(vk_core.device, desc->desc_layout, NULL); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_descriptor ); + + int max_desc_sets = 0; + + VkDescriptorPoolSize dps[] = { + { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = MAX_TEXTURES, + }, { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .descriptorCount = ARRAYSIZE(vk_desc_fixme.ubo_sets), + /* + }, { + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + */ + }, + }; + VkDescriptorPoolCreateInfo dpci = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pPoolSizes = dps, + .poolSizeCount = ARRAYSIZE(dps), + }; + + for (int i = 0; i < ARRAYSIZE(dps); ++i) + max_desc_sets += dps[i].descriptorCount; + + dpci.maxSets = max_desc_sets; + + XVK_CHECK(vkCreateDescriptorPool(vk_core.device, &dpci, NULL, &vk_desc_fixme.pool)); + + { + const int num_sets = MAX_TEXTURES; + // ... TODO find better place for this; this should be per-pipeline/shader + const VkDescriptorSetLayoutBinding bindings[] = { { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = NULL, + }}; + 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); + const VkDescriptorSetAllocateInfo dsai = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = vk_desc_fixme.pool, + .descriptorSetCount = num_sets, + .pSetLayouts = tmp_layouts, + }; + XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc_fixme.one_texture_layout)); + for (int i = 0; i < num_sets; ++i) + tmp_layouts[i] = vk_desc_fixme.one_texture_layout; + + XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc_fixme.texture_sets)); + + Mem_Free(tmp_layouts); + } + + { + const int num_sets = ARRAYSIZE(vk_desc_fixme.ubo_sets); + // ... TODO find better place for this; this should be per-pipeline/shader + VkDescriptorSetLayoutBinding bindings[] = { { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .descriptorCount = 1, + // TODO we use these sets for both vertex-only and fragment-only bindings; improve + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + }}; + 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 = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = vk_desc_fixme.pool, + .descriptorSetCount = num_sets, + .pSetLayouts = tmp_layouts, + }; + XVK_CHECK(vkCreateDescriptorSetLayout(vk_core.device, &dslci, NULL, &vk_desc_fixme.one_uniform_buffer_layout)); + for (int i = 0; i < num_sets; ++i) + tmp_layouts[i] = vk_desc_fixme.one_uniform_buffer_layout; + + XVK_CHECK(vkAllocateDescriptorSets(vk_core.device, &dsai, vk_desc_fixme.ubo_sets)); + + Mem_Free(tmp_layouts); + } + + XRVkModule_OnInitEnd( g_module_descriptor ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_descriptor ); + + vkDestroyDescriptorPool(vk_core.device, vk_desc_fixme.pool, NULL); + vkDestroyDescriptorSetLayout(vk_core.device, vk_desc_fixme.one_texture_layout, NULL); + vkDestroyDescriptorSetLayout(vk_core.device, vk_desc_fixme.one_uniform_buffer_layout, NULL); + + XRVkModule_OnShutdownEnd( g_module_descriptor ); +} \ No newline at end of file diff --git a/ref/vk/vk_descriptor.h b/ref/vk/vk_descriptor.h index f9a39e076..3de27e38c 100644 --- a/ref/vk/vk_descriptor.h +++ b/ref/vk/vk_descriptor.h @@ -1,9 +1,12 @@ #pragma once #include "vk_core.h" +#include "vk_module.h" #include "vk_const.h" +extern RVkModule g_module_descriptor; + // Only used for traditional renderer typedef struct descriptor_pool_s { @@ -20,9 +23,6 @@ typedef struct descriptor_pool_s // FIXME: move to traditional renderer extern descriptor_pool_t vk_desc_fixme; -qboolean VK_DescriptorInit( void ); -void VK_DescriptorShutdown( void ); - struct xvk_image_s; typedef union { VkDescriptorBufferInfo buffer; diff --git a/ref/vk/vk_devmem.c b/ref/vk/vk_devmem.c index 335de9045..d7102dc3d 100644 --- a/ref/vk/vk_devmem.c +++ b/ref/vk/vk_devmem.c @@ -4,6 +4,17 @@ #define MAX_DEVMEM_ALLOCS 32 #define DEFAULT_ALLOCATION_SIZE (64 * 1024 * 1024) +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +RVkModule g_module_devmem = { + .name = "devmem", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_Empty, + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { uint32_t type_index; VkMemoryPropertyFlags property_flags; // device vs host @@ -180,12 +191,18 @@ void VK_DevMemFree(const vk_devmem_t *mem) { } } -qboolean VK_DevMemInit( void ) { +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_devmem ); + g_vk_devmem.verbose = !!gEngine.Sys_CheckParm("-vkdebugmem"); + + XRVkModule_OnInitEnd( g_module_devmem ); return true; } -void VK_DevMemDestroy( void ) { +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_devmem ); + for (int i = 0; i < g_vk_devmem.num_allocs; ++i) { const vk_device_memory_t *const device_memory = g_vk_devmem.allocs + i; ASSERT(device_memory->refcount == 0); @@ -200,4 +217,6 @@ void VK_DevMemDestroy( void ) { } g_vk_devmem.num_allocs = 0; -} + + XRVkModule_OnShutdownEnd( g_module_devmem ); +} \ No newline at end of file diff --git a/ref/vk/vk_devmem.h b/ref/vk/vk_devmem.h index 5c5087beb..26b8ebea9 100644 --- a/ref/vk/vk_devmem.h +++ b/ref/vk/vk_devmem.h @@ -1,8 +1,9 @@ #pragma once + #include "vk_core.h" +#include "vk_module.h" -qboolean VK_DevMemInit( void ); -void VK_DevMemDestroy( void ); +extern RVkModule g_module_devmem; typedef struct vk_devmem_s { VkDeviceMemory device_memory; diff --git a/ref/vk/vk_framectl.c b/ref/vk/vk_framectl.c index 0c419ea47..d56d4b8c2 100644 --- a/ref/vk/vk_framectl.c +++ b/ref/vk/vk_framectl.c @@ -19,6 +19,47 @@ #include +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // R_BeginFrame: R_VkSwapchainAcquire + // submit: R_VkSwapchainPresent + // Impl_Init: R_VkSwapchainSetRenderPassAndDepthFormat_FIXME -- @RenderpassOwnershipMess + &g_module_swapchain, + + // R_BeginFrame: R_VkCombufScopesGet, R_VkCombufBegin + // submit: R_VkCombufEnd + // Impl_Init: R_VkCombufOpen + // Impl_Shutdown: R_VkCombufClose + &g_module_combuf, + + // R_BeginFrame: R_VkStagingFrameBegin + // submit: R_VkStagingFrameEnd + &g_module_staging, + + // R_BeginFrame: VK_RenderBegin + // enqueueRendering: VK_Render_FIXME_Barrier, VK_RenderEndRTX + &g_module_render, + + // VK_RenderFrame: VK_SceneRender + &g_module_scene, + + // enqueueRendering: R_VkOverlay_DrawAndFlip + &g_module_overlay, + + // R_VkReadPixels: R_VkImageCreate, R_VkImageDestroy + &g_module_image +}; + +RVkModule g_module_framectl = { + .name = "framectl", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + extern ref_globals_t *gpGlobals; vk_framectl_t vk_frame = {0}; @@ -417,73 +458,6 @@ void R_EndFrame( void ) APROF_SCOPE_END(frame); } -qboolean VK_FrameCtlInit( void ) -{ - PROFILER_SCOPES(APROF_SCOPE_INIT_EX); - - const VkFormat depth_format = findSupportedImageFormat(depth_formats, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); - - // FIXME move this out to renderers - vk_frame.render_pass.raster = createRenderPass(depth_format, false); - if (vk_core.rtx) - vk_frame.render_pass.after_ray_tracing = createRenderPass(depth_format, true); - - if (!R_VkSwapchainInit(vk_frame.render_pass.raster, depth_format)) - return false; - - for (int i = 0; i < MAX_CONCURRENT_FRAMES; ++i) { - vk_framectl_frame_t *const frame = g_frame.frames + i; - frame->combuf = R_VkCombufOpen(); - - frame->sem_framebuffer_ready = R_VkSemaphoreCreate(); - SET_DEBUG_NAMEF(frame->sem_framebuffer_ready, VK_OBJECT_TYPE_SEMAPHORE, "framebuffer_ready[%d]", i); - frame->sem_done = R_VkSemaphoreCreate(); - SET_DEBUG_NAMEF(frame->sem_done, VK_OBJECT_TYPE_SEMAPHORE, "done[%d]", i); - frame->sem_done2 = R_VkSemaphoreCreate(); - SET_DEBUG_NAMEF(frame->sem_done2, VK_OBJECT_TYPE_SEMAPHORE, "done2[%d]", i); - frame->fence_done = R_VkFenceCreate(true); - SET_DEBUG_NAMEF(frame->fence_done, VK_OBJECT_TYPE_FENCE, "done[%d]", i); - } - - // Signal first frame semaphore as done - { - const VkPipelineStageFlags stageflags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - const VkSubmitInfo subinfo = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .pNext = NULL, - .commandBufferCount = 0, - .pCommandBuffers = NULL, - .waitSemaphoreCount = 0, - .pWaitSemaphores = NULL, - .pWaitDstStageMask = &stageflags, - .signalSemaphoreCount = 1, - .pSignalSemaphores = &g_frame.frames[0].sem_done2, - }; - XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, VK_NULL_HANDLE)); - } - - vk_frame.rtx_enabled = vk_core.rtx; - - return true; -} - -void VK_FrameCtlShutdown( void ) { - for (int i = 0; i < MAX_CONCURRENT_FRAMES; ++i) { - vk_framectl_frame_t *const frame = g_frame.frames + i; - R_VkCombufClose(frame->combuf); - R_VkSemaphoreDestroy(frame->sem_framebuffer_ready); - R_VkSemaphoreDestroy(frame->sem_done); - R_VkSemaphoreDestroy(frame->sem_done2); - R_VkFenceDestroy(frame->fence_done); - } - - R_VkSwapchainShutdown(); - - vkDestroyRenderPass(vk_core.device, vk_frame.render_pass.raster, NULL); - if (vk_core.rtx) - vkDestroyRenderPass(vk_core.device, vk_frame.render_pass.after_ray_tracing, NULL); -} - static qboolean canBlitFromSwapchainToFormat( VkFormat dest_format ) { VkFormatProperties props; @@ -762,3 +736,83 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) filename, (save_end_ns - save_begin_ns) / 1e6, (end_ns - start_ns) / 1e6); return result; } + +static qboolean Impl_Init( void ) { + // Remove this double check when we figure out @RenderpassOwnershipMess + // because this check is already inside `XRVkModule_OnInitStart`. + if ( g_module_framectl.state == RVkModuleState_Initialized ) + return true; + + const VkFormat depth_format = findSupportedImageFormat(depth_formats, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + + // FIXME move this out to renderers + vk_frame.render_pass.raster = createRenderPass(depth_format, false); + if (vk_core.rtx) + vk_frame.render_pass.after_ray_tracing = createRenderPass(depth_format, true); + + // Render pass and depth format have to be set before we begin to init swapchain. + // (Swapchain will be initialized as a dependency/required module) + R_VkSwapchainSetRenderPassAndDepthFormat_FIXME( vk_frame.render_pass.raster, depth_format ); // -- @RenderpassOwnershipMess + + XRVkModule_OnInitStart( g_module_framectl ); + + PROFILER_SCOPES(APROF_SCOPE_INIT_EX); + + for (int i = 0; i < MAX_CONCURRENT_FRAMES; ++i) { + vk_framectl_frame_t *const frame = g_frame.frames + i; + frame->combuf = R_VkCombufOpen(); + + frame->sem_framebuffer_ready = R_VkSemaphoreCreate(); + SET_DEBUG_NAMEF(frame->sem_framebuffer_ready, VK_OBJECT_TYPE_SEMAPHORE, "framebuffer_ready[%d]", i); + frame->sem_done = R_VkSemaphoreCreate(); + SET_DEBUG_NAMEF(frame->sem_done, VK_OBJECT_TYPE_SEMAPHORE, "done[%d]", i); + frame->sem_done2 = R_VkSemaphoreCreate(); + SET_DEBUG_NAMEF(frame->sem_done2, VK_OBJECT_TYPE_SEMAPHORE, "done2[%d]", i); + frame->fence_done = R_VkFenceCreate(true); + SET_DEBUG_NAMEF(frame->fence_done, VK_OBJECT_TYPE_FENCE, "done[%d]", i); + } + + // Signal first frame semaphore as done + { + const VkPipelineStageFlags stageflags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + const VkSubmitInfo subinfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = NULL, + .commandBufferCount = 0, + .pCommandBuffers = NULL, + .waitSemaphoreCount = 0, + .pWaitSemaphores = NULL, + .pWaitDstStageMask = &stageflags, + .signalSemaphoreCount = 1, + .pSignalSemaphores = &g_frame.frames[0].sem_done2, + }; + XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, VK_NULL_HANDLE)); + } + + vk_frame.rtx_enabled = vk_core.rtx; + + XRVkModule_OnInitEnd( g_module_framectl ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_framectl ); + + for (int i = 0; i < MAX_CONCURRENT_FRAMES; ++i) { + vk_framectl_frame_t *const frame = g_frame.frames + i; + R_VkCombufClose(frame->combuf); + R_VkSemaphoreDestroy(frame->sem_framebuffer_ready); + R_VkSemaphoreDestroy(frame->sem_done); + R_VkSemaphoreDestroy(frame->sem_done2); + R_VkFenceDestroy(frame->fence_done); + } + + // R_VkSwapchainShutdown(); + // Do we need explicit shutdown? + + vkDestroyRenderPass(vk_core.device, vk_frame.render_pass.raster, NULL); + if (vk_core.rtx) + vkDestroyRenderPass(vk_core.device, vk_frame.render_pass.after_ray_tracing, NULL); + + XRVkModule_OnShutdownEnd( g_module_framectl ); +} \ No newline at end of file diff --git a/ref/vk/vk_framectl.h b/ref/vk/vk_framectl.h index 47e8dac1d..5a04de014 100644 --- a/ref/vk/vk_framectl.h +++ b/ref/vk/vk_framectl.h @@ -1,10 +1,14 @@ #pragma once + #include "vk_core.h" +#include "vk_module.h" #include "xash3d_types.h" #define MAX_CONCURRENT_FRAMES 2 +extern RVkModule g_module_framectl; + // TODO most of the things below should not be global. Instead, they should be passed as an argument/context to all the drawing functions that want this info typedef struct vk_framectl_s { // TODO only used from 2d and r_speeds, remove @@ -26,9 +30,6 @@ typedef struct vk_framectl_s { extern vk_framectl_t vk_frame; -qboolean VK_FrameCtlInit( void ); -void VK_FrameCtlShutdown( void ); - void R_BeginFrame( qboolean clearScene ); void VK_RenderFrame( const struct ref_viewpass_s *rvp ); void R_EndFrame( void ); diff --git a/ref/vk/vk_geometry.c b/ref/vk/vk_geometry.c index d9725381e..1ce149943 100644 --- a/ref/vk/vk_geometry.c +++ b/ref/vk/vk_geometry.c @@ -15,6 +15,30 @@ #define GEOMETRY_BUFFER_SIZE (GEOMETRY_BUFFER_STATIC_SIZE + GEOMETRY_BUFFER_DYNAMIC_SIZE) +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // Impl_Init: VK_BufferCreate + // Impl_Shutdown: VK_BufferDestroy + &g_module_buffer, + + // R_GeometryRangeLock: R_VkStagingLockForBuffer + // R_GeometryRangeLockSubrange: R_VkStagingLockForBuffer + // R_GeometryRangeUnlock: R_VkStagingUnlock + // R_GeometryBufferAllocOnceAndLock: R_VkStagingLockForBuffer + // R_GeometryBufferUnlock: R_VkStagingUnlock + &g_module_staging +}; + +RVkModule g_module_geometry = { + .name = "geometry", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + // TODO profiler counters static struct { @@ -175,13 +199,27 @@ void R_GeometryBuffer_MapClear( void ) { // allocated blocks count remains constant and doesn't grow between maps } -qboolean R_GeometryBuffer_Init(void) { +void R_GeometryBuffer_Flip(void) { + R_BlocksClearOnce(&g_geom.alloc); +} + +VkBuffer R_GeometryBuffer_Get(void) { + return g_geom.buffer.buffer; +} + +qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_geometry ); + // TODO device memory and friends (e.g. handle mobile memory ...) if (!VK_BufferCreate("geometry buffer", &g_geom.buffer, GEOMETRY_BUFFER_SIZE, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | (vk_core.rtx ? VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0), (vk_core.rtx ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0))) + { + gEngine.Con_Printf( S_ERROR "Couldn't create geometry buffer.\n" ); + g_module_geometry.state = RVkModuleState_NotInitialized; return false; + } #define EXPECTED_ALLOCS 1024 R_BlocksCreate(&g_geom.alloc, GEOMETRY_BUFFER_SIZE, GEOMETRY_BUFFER_DYNAMIC_SIZE, EXPECTED_ALLOCS); @@ -191,18 +229,16 @@ qboolean R_GeometryBuffer_Init(void) { R_SPEEDS_METRIC(g_geom.stats.indices, "indices", kSpeedsMetricCount); R_SPEEDS_COUNTER(g_geom.stats.dyn_vertices, "dyn_vertices", kSpeedsMetricCount); R_SPEEDS_COUNTER(g_geom.stats.dyn_indices, "dyn_indices", kSpeedsMetricCount); + + XRVkModule_OnInitEnd( g_module_geometry ); return true; } -void R_GeometryBuffer_Shutdown(void) { +void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_geometry ); + R_BlocksDestroy(&g_geom.alloc); VK_BufferDestroy( &g_geom.buffer ); -} - -void R_GeometryBuffer_Flip(void) { - R_BlocksClearOnce(&g_geom.alloc); -} -VkBuffer R_GeometryBuffer_Get(void) { - return g_geom.buffer.buffer; + XRVkModule_OnShutdownEnd( g_module_geometry ); } diff --git a/ref/vk/vk_geometry.h b/ref/vk/vk_geometry.h index 706379880..a95d84050 100644 --- a/ref/vk/vk_geometry.h +++ b/ref/vk/vk_geometry.h @@ -1,10 +1,15 @@ #pragma once + +#include "vk_core.h" +#include "vk_module.h" + #include "vk_common.h" #include "r_block.h" -#include "vk_core.h" #include +extern RVkModule g_module_geometry; + // General buffer usage pattern // 1. alloc (allocates buffer mem, stores allocation data) // 2. (returns void* buf and handle) write to buf @@ -83,9 +88,6 @@ void R_GeometryBufferUnlock( const r_geometry_buffer_lock_t *lock ); void R_GeometryBuffer_MapClear( void ); // Free the entire buffer for a new map -qboolean R_GeometryBuffer_Init(void); -void R_GeometryBuffer_Shutdown(void); - void R_GeometryBuffer_Flip(void); // FIXME is there a better way? diff --git a/ref/vk/vk_image.c b/ref/vk/vk_image.c index 11297ee27..f0518a8ed 100644 --- a/ref/vk/vk_image.c +++ b/ref/vk/vk_image.c @@ -8,6 +8,28 @@ // Long type lists functions #include "vk_image_extra.h" +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // R_VkImageCreate: VK_DevMemAllocate + // R_VkImageDestroy: VK_DevMemFree + &g_module_devmem, + + // R_VkImageUploadBegin: R_VkStagingGetCommandBuffer + // R_VkImageUploadSlice: R_VkStagingLockForImage, R_VkStagingUnlock + // R_VkImageUploadEnd: R_VkStagingCommit + &g_module_staging +}; + +RVkModule g_module_image = { + .name = "image", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + static const VkImageUsageFlags usage_bits_implying_views = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | @@ -321,3 +343,20 @@ void R_VkImageUploadEnd( r_vk_image_t *img ) { VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_barrier); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_image ); + + // Nothing to init for now. + + XRVkModule_OnInitEnd( g_module_image ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_image ); + + // Nothing to clear for now. + + XRVkModule_OnShutdownEnd( g_module_image ); +} diff --git a/ref/vk/vk_image.h b/ref/vk/vk_image.h index 55da9f924..cdf58da9e 100644 --- a/ref/vk/vk_image.h +++ b/ref/vk/vk_image.h @@ -1,7 +1,12 @@ #pragma once + #include "vk_core.h" +#include "vk_module.h" + #include "vk_devmem.h" +extern RVkModule g_module_image; + typedef struct r_vk_image_s { vk_devmem_t devmem; VkImage image; diff --git a/ref/vk/vk_light.c b/ref/vk/vk_light.c index 5bbb63015..2503e2fbf 100644 --- a/ref/vk/vk_light.c +++ b/ref/vk/vk_light.c @@ -38,6 +38,30 @@ PROFILER_SCOPES(SCOPE_DECLARE) #undef SCOPE_DECLARE +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // Impl_Init: VK_BufferCreate + // Impl_Shutdown: VK_BufferDestroy + &g_module_buffer, + + // loadRadData: R_TextureFindByNameF + &g_module_textures_api, + + // uploadGridRange: R_VkStagingLockForBuffer, R_VkStagingUnlock + // VK_LightsUpload: R_VkStagingLockForBuffer, R_VkStagingUnlock + &g_module_staging +}; + +RVkModule g_module_light = { + .name = "light", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { vec3_t emissive; qboolean set; @@ -94,40 +118,6 @@ static void debugDumpLights( void ) { vk_lights_t g_lights = {0}; -qboolean VK_LightsInit( void ) { - PROFILER_SCOPES(APROF_SCOPE_INIT); - - gEngine.Cmd_AddCommand("rt_debug_lights_dump", debugDumpLights, "Dump all light sources for next frame"); - - const int buffer_size = sizeof(struct LightsMetadata) + sizeof(struct LightCluster) * MAX_LIGHT_CLUSTERS; - - if (!VK_BufferCreate("rt lights buffer", &g_lights_.buffer, buffer_size, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { - // FIXME complain, handle - return false; - } - - R_SPEEDS_COUNTER(g_lights_.stats.dirty_cells, "dirty_cells", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_lights_.stats.dirty_cells_size, "dirty_cells_size", kSpeedsMetricBytes); - R_SPEEDS_COUNTER(g_lights_.stats.ranges_uploaded, "ranges_uploaded", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_lights_.num_polygons, "polygons", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_lights_.num_point_lights, "points", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_lights_.stats.dynamic_polygons, "polygons_dynamic", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_lights_.stats.dynamic_points, "points_dynamic", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_lights_.stats.dlights, "dlights", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_lights_.stats.elights, "elights", kSpeedsMetricCount); - - return true; -} - -void VK_LightsShutdown( void ) { - VK_BufferDestroy(&g_lights_.buffer); - - gEngine.Cmd_RemoveCommand("vk_lights_dump"); - bitArrayDestroy(&g_lights_.visited_cells); -} - typedef struct { int num; int leafs[]; @@ -1394,3 +1384,46 @@ void RT_LightsFrameEnd( void ) { debug_dump_lights.enabled = false; APROF_SCOPE_END(finalize); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_light ); + + PROFILER_SCOPES(APROF_SCOPE_INIT); + + gEngine.Cmd_AddCommand("rt_debug_lights_dump", debugDumpLights, "Dump all light sources for next frame"); + + const int buffer_size = sizeof(struct LightsMetadata) + sizeof(struct LightCluster) * MAX_LIGHT_CLUSTERS; + + if (!VK_BufferCreate("rt lights buffer", &g_lights_.buffer, buffer_size, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + // FIXME complain, handle -- NOTE(nilsoncore): Is it done? + ERR( "Couldn't create Ray Tracing lights buffer." ); + g_module_light.state = RVkModuleState_NotInitialized; + return false; + } + + R_SPEEDS_COUNTER(g_lights_.stats.dirty_cells, "dirty_cells", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_lights_.stats.dirty_cells_size, "dirty_cells_size", kSpeedsMetricBytes); + R_SPEEDS_COUNTER(g_lights_.stats.ranges_uploaded, "ranges_uploaded", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_lights_.num_polygons, "polygons", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_lights_.num_point_lights, "points", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_lights_.stats.dynamic_polygons, "polygons_dynamic", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_lights_.stats.dynamic_points, "points_dynamic", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_lights_.stats.dlights, "dlights", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_lights_.stats.elights, "elights", kSpeedsMetricCount); + + XRVkModule_OnInitEnd( g_module_light ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_light ); + + VK_BufferDestroy(&g_lights_.buffer); + + gEngine.Cmd_RemoveCommand("vk_lights_dump"); + bitArrayDestroy(&g_lights_.visited_cells); + + XRVkModule_OnShutdownEnd( g_module_light ); +} diff --git a/ref/vk/vk_light.h b/ref/vk/vk_light.h index 8e74b99df..7f05954ef 100644 --- a/ref/vk/vk_light.h +++ b/ref/vk/vk_light.h @@ -2,9 +2,12 @@ #include "vk_const.h" #include "vk_core.h" +#include "vk_module.h" #include "xash3d_types.h" +extern RVkModule g_module_light; + typedef struct { uint8_t num_point_lights; uint8_t num_polygons; @@ -63,9 +66,6 @@ typedef struct { extern vk_lights_t g_lights; -qboolean VK_LightsInit( void ); -void VK_LightsShutdown( void ); - // Allocate clusters and vis data for the new map struct model_s; void RT_LightsNewMap( const struct model_s *map ); diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index 2f68e0f80..041f409e1 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -15,6 +15,28 @@ #define MAX_MATERIALS 2048 #define MAX_NEW_MATERIALS 128 +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // loadTexture: R_TextureUploadFromFileExAcquire + // acquireTexturesForMaterial: R_TextureAcquire + // releaseTexturesForMaterialPtr: R_TextureRelease + // assignMaterialForTexture: R_TextureGetNameByIndex + // loadMaterialsFromFile: R_TextureAcquire, R_TextureGetNameByIndex + // R_VkMaterialGetForTextureWithFlags: R_TextureGetNameByIndex + // R_VkMaterialGetForName: R_TextureFindByNameLike + &g_module_textures_api +}; + +RVkModule g_module_materials = { + .name = "materials", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + static r_vk_material_t k_default_material = { .tex_base_color = -1, .tex_metalness = 0, @@ -634,7 +656,19 @@ qboolean R_VkMaterialGetEx( int tex_id, int rendermode, r_vk_material_t *out_mat return false; } -void R_VkMaterialsShutdown( void ) { - materialsReleaseTextures(); +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_materials ); + + // Nothing to init for now. + + XRVkModule_OnInitEnd( g_module_materials ); + return true; } +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_materials ); + + materialsReleaseTextures(); + + XRVkModule_OnShutdownEnd( g_module_materials ); +} diff --git a/ref/vk/vk_materials.h b/ref/vk/vk_materials.h index 49eef84ff..77e1ea501 100644 --- a/ref/vk/vk_materials.h +++ b/ref/vk/vk_materials.h @@ -1,7 +1,11 @@ #pragma once +#include "vk_module.h" + #include "xash3d_types.h" +extern RVkModule g_module_materials; + /* TODO #define MATERIAL_FIELDS_LIST(X) \ X(0, int, tex_base_color, basecolor_map, readTexture) \ @@ -34,8 +38,6 @@ typedef struct { int index; } r_vk_material_ref_t; // TODO: track "version" in high bits? void R_VkMaterialsReload( void ); -void R_VkMaterialsShutdown( void ); - struct model_s; void R_VkMaterialsLoadForModel( const struct model_s* mod ); diff --git a/ref/vk/vk_module.c b/ref/vk/vk_module.c new file mode 100644 index 000000000..fb529f3bd --- /dev/null +++ b/ref/vk/vk_module.c @@ -0,0 +1,150 @@ +#include "vk_module.h" +#include "debugbreak.h" + +// NOTE(nilsoncore): +// These `replica` functions are broken for now because I didn't update them to be exact as `XRVkModule_Onxxx` macros. +// The reason why these functions exist (for now) is that I don't know whether we should use macros or functions like these. +// For now we just wrap around every `Impl_Init` module function with `XRVkModule_OnxxxStart` and `XRVkModule_OnxxxEnd`. +// +// This approach is quite simple and clean: you just write this construction passing it module and that's it. +// You don't have to do anything else and know how it works inside (for the most part). +// Although, it has a downside: these macros are CHUNKY. We don't just call some function, we paste the whole code inline +// for every module out there. It probably grows the binary size a lot (didn't measure). +// +// The second approach is to use regular functions. It is probably more conventional and rational way, but it has downsides too. +// You can't just write it and expect it to work. The placing is the same as with the macros, but things start to become tricky. +// We can't use `qboolean` as a result value because of the `already initialized?` check. +// If module is already initialized we return `true` from the module `Impl_Init` function. Here inside `OnInitStart` it's too ambigious - +// does that mean that module is already initialized and we are done or module has loaded dependencies and ready to initialize? +// In this case we should probably declare some enum with states like `_AlreadyInitialized`, `_DependenciesLoaded`, `_FailedToLoadDependecies`, etc. +// In the end we will end up not just with the boilerplate code that we have to paste everywhere, but also we can't make change in one place -- +// every change must be then made manually in every file. +// +// Whether we want it or not -- we must decide which approach we will use. Don't be afraid to comment on this if I missed something. + +qboolean RVkModule_OnInitStart( RVkModule *module ) { + if ( module->state == RVkModuleState_Initialized ) + return true; + + gEngine.Con_Printf( "RVkModule: Initializing '%s'...\n", module->name ); + module->state = RVkModuleState_IsInitializing; + + qboolean deps_inited = RVkModule_InitDependencies( module ); + if ( !deps_inited ) { + module->state = RVkModuleState_NotInitialized; + return false; + } + + return true; +} + +void RVkModule_OnInitEnd( RVkModule *module ) { + module->state = RVkModuleState_Initialized; + gEngine.Con_Printf( "RVkModule: '%s' has been initialized.\n", module->name ); +} + +qboolean RVkModule_OnShutdownStart( RVkModule *module ) { + if ( module->state != RVkModuleState_Initialized ) + return false; + + gEngine.Con_Printf( "RVkModule: Shutting down '%s'...\n", module->name ); + return true; +} + +void RVkModule_OnShutdownEnd( RVkModule *module ) { + module->state = RVkModuleState_Shutdown; + gEngine.Con_Printf( "RVkModule: '%s' has been shut down.\n", module->name ); +} + +qboolean RVkModule_InitDependencies( RVkModule *module ) { + ASSERT( module && "Module pointer must be valid" ); + + const uint32_t deps_count = module->dependencies.count; + qboolean second_try = false; + uint32_t deps_in_progress_count = 0; + +init_dependencies: + for ( uint32_t dep_module_index = 0; dep_module_index < deps_count; dep_module_index += 1 ) { + RVkModule *const dep_module = module->dependencies.modules[dep_module_index]; + + if ( dep_module == module ) { + // NOTE(nilsoncore): Just exit with error because module dependencies are + // set statically, so this must be fixed in source code. + gEngine.Host_Error( "RVkModule: Failed to init dependencies for '%s' - module depends on itself.\n", module->name ); + } + + if ( dep_module->state == RVkModuleState_IsInitializing ) { + if ( second_try ) { + RVkModule_PrintDependencyList( module, true ); + RVkModule_PrintDependencyList( dep_module, true ); + // NOTE(nilsoncore): Just exit with error because module dependencies are + // set statically, so this must be fixed in source code. + gEngine.Host_Error( "RVkModule: Failed to init dependencies for '%s' - module is stuck on '%s' (it is a circular dependency chain).\n", module->name, dep_module->name ); + } else { + deps_in_progress_count += 1; + continue; + } + } + + if ( dep_module->init_caller == NULL && + dep_module->state != RVkModuleState_Initialized && + dep_module->state != RVkModuleState_IsInitializing ) + { + dep_module->init_caller = module; + } + + if ( !dep_module->Init() ) { + gEngine.Con_Printf( S_ERROR "Module '%s' failed to init dependency '%s'.\n", module->name, dep_module->name ); + return false; + } + } + + second_try = ( deps_in_progress_count > 0 ); + if ( second_try ) + goto init_dependencies; + + return true; +} + +void RVkModule_PrintDependencyList( RVkModule *module, qboolean print_states ) { + ASSERT( module && "Module pointer must be valid" ); + + const uint32_t deps_count = module->dependencies.count; + if ( deps_count == 0 ) { + gEngine.Con_Printf( "Module '%s' has 0 dependencies.\n", module->name ); + } else { + gEngine.Con_Printf( "Module '%s' has %u dependenc%s: [", module->name, deps_count, ( deps_count == 1 ) ? "y" : "ies" ); + + // #0 + uint32_t dep_module_index = 0; + RVkModule *const dep_module = module->dependencies.modules[dep_module_index]; + if ( print_states ) gEngine.Con_Printf( "%s (%s)", dep_module->name, RVkModule_GetStateName( dep_module->state ) ); + else gEngine.Con_Printf( "%s", dep_module->name ); + + // #1..deps_count + for ( dep_module_index = 1; dep_module_index < deps_count; dep_module_index += 1 ) { + RVkModule *const dep_module = module->dependencies.modules[dep_module_index]; + if ( print_states ) gEngine.Con_Printf( ", %s (%s)", dep_module->name, RVkModule_GetStateName( dep_module->state ) ); + else gEngine.Con_Printf( ", %s", dep_module->name ); + } + + gEngine.Con_Printf( "]\n" ); + } +} + +void RVkModule_PrintDependencyTree( RVkModule *module ) { + ASSERT( module && "Module pointer must be valid" ); + + gEngine.Con_Printf( "Dependency tree of module '%s':\n", module->name ); + // TODO(nilsoncore) +} + +const char *RVkModule_GetStateName( RVkModuleState state ) { + switch ( state ) { + case RVkModuleState_NotInitialized: return "NotInitialized"; + case RVkModuleState_IsInitializing: return "IsInitializing"; + case RVkModuleState_Initialized: return "Initialized"; + case RVkModuleState_Shutdown: return "Shutdown"; + default: return "(invalid)"; + } +} \ No newline at end of file diff --git a/ref/vk/vk_module.h b/ref/vk/vk_module.h new file mode 100644 index 000000000..c86439fb6 --- /dev/null +++ b/ref/vk/vk_module.h @@ -0,0 +1,121 @@ +/* + vk_module.h - ref/vk modules' API + + TODO(nilsoncore): Explain what this is. +*/ + +#pragma once +#ifndef VK_MODULE_H +#define VK_MODULE_H + +#include "vk_common.h" + +typedef uint8_t RVkModuleState; +enum { + RVkModuleState_NotInitialized = 0, + RVkModuleState_IsInitializing = 1, + RVkModuleState_Initialized = 2, + RVkModuleState_Shutdown = 3, + + RVkModuleState_COUNT +}; + +/* NOTE(nilsoncore): Unused for now. We should decide whether we keep this or not. +typedef uint8_t RVkModuleLogLevels; +enum { + RVkModuleLogLevels_None = 0, + RVkModuleLogLevels_Error = (1 << 0), + RVkModuleLogLevels_Warning = (1 << 1), + RVkModuleLogLevels_Info = (1 << 2), + RVkModuleLogLevels_Debug = (1 << 3), + + // Shortcuts: + RVkModuleLogLevels_AllExceptDebug = RVkModuleLogLevels_Error | + RVkModuleLogLevels_Warning | + RVkModuleLogLevels_Info, + + RVkModuleLogLevels_All = RVkModuleLogLevels_AllExceptDebug | + RVkModuleLogLevels_Debug +}; +*/ + +// Forward declare it so we can use it right below before we actually declare its' body. +typedef struct RVkModule RVkModule; + +typedef struct RVkModuleDependencies { + RVkModule **modules; + uint32_t count; +} RVkModuleDependencies; + +#define RVkModuleDependencies_Empty { .modules = NULL, .count = 0 } +#define RVkModuleDependencies_FromStaticArray( array ) { .modules = array, .count = ( uint32_t ) COUNTOF( array ) } + +typedef qboolean ( *RVkModulePfn_Init )( void ); +typedef void ( *RVkModulePfn_Shutdown )( void ); + +#define XRVkModule_OnInitStart( module ) do { \ + if ( module.state == RVkModuleState_Initialized ) { \ + return true; \ + } \ + if ( module.init_caller != NULL ) { \ + gEngine.Con_Printf( "RVkModule: Initializing '%s' from '%s'...\n", module.name, module.init_caller->name ); \ + } else { \ + gEngine.Con_Printf( "RVkModule: Initializing '%s'...\n", module.name ); \ + } \ + module.state = RVkModuleState_IsInitializing; \ + qboolean deps_inited = RVkModule_InitDependencies( &module ); \ + if ( !deps_inited ) { \ + gEngine.Con_Printf( S_ERROR "RVkModule: Failed to init dependencies for '%s'.\n", module.name ); \ + module.state = RVkModuleState_NotInitialized; \ + return false; \ + } \ +} while(0) + +#define XRVkModule_OnInitEnd( module ) do { \ + module.state = RVkModuleState_Initialized; \ + if ( module.init_caller != NULL ) { \ + gEngine.Con_Printf( "RVkModule: '%s' from '%s' has been initialized.\n", module.name, module.init_caller->name ); \ + } else { \ + gEngine.Con_Printf( "RVkModule: '%s' has been initialized.\n", module.name ); \ + } \ +} while(0) + +#define XRVkModule_OnShutdownStart( module ) do { \ + if ( module.state != RVkModuleState_Initialized ) { \ + return; \ + } \ + gEngine.Con_Printf( "RVkModule: Shutting down '%s'...\n", module.name ); \ +} while(0) + +#define XRVkModule_OnShutdownEnd( module ) do { \ + module.state = RVkModuleState_Shutdown; \ + gEngine.Con_Printf( "RVkModule: '%s' has been shut down.\n", module.name ); \ +} while(0) + +typedef struct RVkModule { + const char *name; + RVkModuleState state; + + RVkModuleDependencies dependencies; + uint32_t reference_count; + + RVkModule *init_caller; + + RVkModulePfn_Init Init; + RVkModulePfn_Shutdown Shutdown; + + // NOTE(nilsoncore): Unused for now. See comment above. + // RVkModuleLogLevels log_levels; +} RVkModule; + +qboolean RVkModule_OnInitStart( RVkModule *module ); +void RVkModule_OnInitEnd( RVkModule *module); +qboolean RVkModule_OnShutdownStart( RVkModule *module ); +void RVkModule_OnShutdownEnd( RVkModule *module ); + +qboolean RVkModule_InitDependencies( RVkModule *module ); +void RVkModule_PrintDependencyList( RVkModule *module, qboolean print_states ); +void RVkModule_PrintDependencyTree( RVkModule *module ); // TODO(nilsoncore) +const char *RVkModule_GetStateName( RVkModuleState state ); + +#endif /* VK_MODULE_H */ \ No newline at end of file diff --git a/ref/vk/vk_nv_aftermath.c b/ref/vk/vk_nv_aftermath.c index 7c57d6411..ce8005821 100644 --- a/ref/vk/vk_nv_aftermath.c +++ b/ref/vk/vk_nv_aftermath.c @@ -3,8 +3,6 @@ #include "vk_common.h" #include "vk_core.h" -#include "xash3d_types.h" - #ifdef USE_AFTERMATH #include "GFSDK_Aftermath.h" #include "GFSDK_Aftermath_GpuCrashDump.h" @@ -15,6 +13,17 @@ #define MAX_NV_CHECKPOINTS 2048 +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +RVkModule g_module_nv_aftermath = { + .name = "nv_aftermath", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_Empty, + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { unsigned sequence; char message[256]; @@ -118,27 +127,6 @@ static void callbackResolveMarkers(const void* pMarker, void* pUserData, void** *markerSize = strlen(msg); } -static qboolean initialized = false; -qboolean VK_AftermathInit() { - AM_CHECK(GFSDK_Aftermath_EnableGpuCrashDumps( - GFSDK_Aftermath_Version_API, - GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, - GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks, - callbackGpuCrashDump, - callbackShaderDebugInfo, - callbackGpuCrashDumpDescription, - callbackResolveMarkers, - NULL)); - - initialized = true; - return true; -} - -void VK_AftermathShutdown() { - if (initialized) { - GFSDK_Aftermath_DisableGpuCrashDumps(); - } -} #endif //ifdef USE_AFTERMATH void R_Vk_NV_CheckpointF(VkCommandBuffer cmdbuf, const char *fmt, ...) { @@ -184,3 +172,35 @@ void R_Vk_NV_Checkpoint_Dump(void) { } } +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_nv_aftermath ); + +#ifdef USE_AFTERMATH + AM_CHECK(GFSDK_Aftermath_EnableGpuCrashDumps( + GFSDK_Aftermath_Version_API, + GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, + GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks, + callbackGpuCrashDump, + callbackShaderDebugInfo, + callbackGpuCrashDumpDescription, + callbackResolveMarkers, + NULL)); + gEngine.Con_Printf( S_NOTE "Module '%s' is initialized with enabled functionality.\n", g_module_nv_aftermath.name ); +#else /* USE_AFTERMATH */ + gEngine.Con_Printf( S_WARN "Module '%s' is initialized with disabled functionality.\n", g_module_nv_aftermath.name ); + gEngine.Con_Printf( S_WARN "Define USE_AFTERMATH macro to enable it.\n" ); +#endif /* USE_AFTERMATH */ + + XRVkModule_OnInitEnd( g_module_nv_aftermath ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_nv_aftermath ); + +#ifdef USE_AFTERMATH + GFSDK_Aftermath_DisableGpuCrashDumps(); +#endif + + XRVkModule_OnShutdownEnd( g_module_nv_aftermath ); +} diff --git a/ref/vk/vk_nv_aftermath.h b/ref/vk/vk_nv_aftermath.h index 0a0f8553f..af482b9ca 100644 --- a/ref/vk/vk_nv_aftermath.h +++ b/ref/vk/vk_nv_aftermath.h @@ -1,14 +1,13 @@ #pragma once +#include "vk_module.h" + #include "xash3d_types.h" #define VK_NO_PROTOTYPES #include -#ifdef USE_AFTERMATH -qboolean VK_AftermathInit(); -void VK_AftermathShutdown(); -#endif +extern RVkModule g_module_nv_aftermath; void R_Vk_NV_CheckpointF(VkCommandBuffer cmdbuf, const char *fmt, ...); void R_Vk_NV_Checkpoint_Dump(void); diff --git a/ref/vk/vk_overlay.c b/ref/vk/vk_overlay.c index 33ef183c5..bf3f90c58 100644 --- a/ref/vk/vk_overlay.c +++ b/ref/vk/vk_overlay.c @@ -13,6 +13,32 @@ #include "com_strings.h" #include "eiface.h" +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // Impl_Init: VK_BufferCreate + // Impl_Shutdown: VK_BufferDestroy + &g_module_buffer, + + // drawFill: R_TextureFindByName + // R_DrawTileClear: R_TextureGetNameByIndex + &g_module_textures_api, + + // drawOverlay: R_VkTextureGetDescriptorUnorm + // &g_module_textures, -- @TexturesInitHardcode + + // createPipelines: VK_PipelineGraphicsCreate + &g_module_pipeline +}; + +RVkModule g_module_overlay = { + .name = "overlay", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; typedef struct vertex_2d_s { float x, y; @@ -231,29 +257,6 @@ static qboolean createPipelines( void ) return true; } -qboolean R_VkOverlay_Init( void ) { - if (!createPipelines()) - return false; - - // TODO this doesn't need to be host visible, could use staging too - if (!VK_BufferCreate("2d pics_buffer", &g2d.pics_buffer, sizeof(vertex_2d_t) * MAX_VERTICES, - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT )) - // FIXME cleanup - return false; - - R_FlippingBuffer_Init(&g2d.pics_buffer_alloc, MAX_VERTICES); - - return true; -} - -void R_VkOverlay_Shutdown( void ) { - VK_BufferDestroy(&g2d.pics_buffer); - for (int i = 0; i < ARRAYSIZE(g2d.pipelines); ++i) - vkDestroyPipeline(vk_core.device, g2d.pipelines[i], NULL); - - vkDestroyPipelineLayout(vk_core.device, g2d.pipeline_layout, NULL); -} - static void drawOverlay( VkCommandBuffer cmdbuf ) { DEBUG_BEGIN(cmdbuf, "2d overlay"); @@ -303,3 +306,38 @@ void CL_FillRGBABlend( float x, float y, float w, float h, int r, int g, int b, { drawFill(x, y, w, h, r, g, b, a, kRenderTransColor); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_overlay ); + + if (!createPipelines()) { + g_module_overlay.state = RVkModuleState_NotInitialized; + return false; + } + + // TODO this doesn't need to be host visible, could use staging too + if (!VK_BufferCreate("2d pics_buffer", &g2d.pics_buffer, sizeof(vertex_2d_t) * MAX_VERTICES, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT )) { + // FIXME cleanup -- NOTE(nilsoncore): Is it done? + gEngine.Con_Printf(S_ERROR "Couldn't create 2D pictures buffer.\n"); + g_module_overlay.state = RVkModuleState_NotInitialized; + return false; + } + + R_FlippingBuffer_Init(&g2d.pics_buffer_alloc, MAX_VERTICES); + + XRVkModule_OnInitEnd( g_module_overlay ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_overlay ); + + VK_BufferDestroy(&g2d.pics_buffer); + for (int i = 0; i < ARRAYSIZE(g2d.pipelines); ++i) + vkDestroyPipeline(vk_core.device, g2d.pipelines[i], NULL); + + vkDestroyPipelineLayout(vk_core.device, g2d.pipeline_layout, NULL); + + XRVkModule_OnShutdownEnd( g_module_overlay ); +} diff --git a/ref/vk/vk_overlay.h b/ref/vk/vk_overlay.h index 5e2b3f0a1..18ba2b1ef 100644 --- a/ref/vk/vk_overlay.h +++ b/ref/vk/vk_overlay.h @@ -1,15 +1,16 @@ #pragma once #include "vk_core.h" +#include "vk_module.h" + #include "xash3d_types.h" +extern RVkModule g_module_overlay; + void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, const byte *data, qboolean dirty ); void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, int texnum ); void R_DrawTileClear( int texnum, int x, int y, int w, int h ); void CL_FillRGBA( float x, float y, float w, float h, int r, int g, int b, int a ); void CL_FillRGBABlend( float x, float y, float w, float h, int r, int g, int b, int a ); -qboolean R_VkOverlay_Init( void ); -void R_VkOverlay_Shutdown( void ); - void R_VkOverlay_DrawAndFlip( VkCommandBuffer cmdbuf, qboolean draw ); diff --git a/ref/vk/vk_pipeline.c b/ref/vk/vk_pipeline.c index d637d39a8..3df3780a9 100644 --- a/ref/vk/vk_pipeline.c +++ b/ref/vk/vk_pipeline.c @@ -1,30 +1,39 @@ #include "vk_pipeline.h" -#include "vk_framectl.h" // VkRenderPass +#include "vk_framectl.h" // VkRenderPass -- @RenderpassOwnershipMess #include "vk_combuf.h" #include "eiface.h" #define MAX_STAGES 2 -VkPipelineCache g_pipeline_cache; +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); -qboolean VK_PipelineInit( void ) -{ - VkPipelineCacheCreateInfo pcci = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, - .initialDataSize = 0, - .pInitialData = NULL, - }; +static RVkModule *required_modules[] = { + // VK_PipelineRayTracingTrace: R_VkCombufScopeBegin, R_VkCombufScopeEnd + &g_module_combuf, - XVK_CHECK(vkCreatePipelineCache(vk_core.device, &pcci, NULL, &g_pipeline_cache)); - return true; -} + // VK_PipelineRayTracingCreate: VK_BufferCreate + // VK_PipelineRayTracingDestroy: VK_BufferDestroy + &g_module_buffer, -void VK_PipelineShutdown( void ) -{ - vkDestroyPipelineCache(vk_core.device, g_pipeline_cache, NULL); -} + // VK_PipelineGraphicsCreate: `.renderPass = vk_frame.render_pass.raster` -- @RenderpassOwnershipMess + // NOTE(nilsoncore): Because of this we can't include `framectl` as a dependency + // as it will create circular dependency chain: `render` -> `pipeline` -> `framectl` -> `render`. + // We have to assume that `framectl` is initialized by someone. Ugh. + // &g_module_framectl +}; + +RVkModule g_module_pipeline = { + .name = "pipeline", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + +VkPipelineCache g_pipeline_cache; VkShaderModule R_VkShaderLoadFromMem(const void *ptr, uint32_t size, const char *name) { if ((size % 4 != 0) || (((uintptr_t)ptr & 3) != 0)) { @@ -159,7 +168,7 @@ VkPipeline VK_PipelineGraphicsCreate(const vk_pipeline_graphics_create_info_t *c .pColorBlendState = &color_blend, .pDepthStencilState = &depth, .layout = ci->layout, - .renderPass = vk_frame.render_pass.raster, + .renderPass = vk_frame.render_pass.raster, // -- @RenderpassOwnershipMess .pDynamicState = &dynamic_state_create_info, .subpass = 0, }; @@ -376,3 +385,26 @@ void VK_PipelineRayTracingTrace(vk_combuf_t *combuf, const vk_pipeline_ray_t *pi vkCmdTraceRaysKHR(combuf->cmdbuf, &pipeline->sbt.raygen, &pipeline->sbt.miss, &pipeline->sbt.hit, &pipeline->sbt.callable, width, height, 1 ); R_VkCombufScopeEnd(combuf, begin_id, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_pipeline ); + + VkPipelineCacheCreateInfo pcci = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, + .initialDataSize = 0, + .pInitialData = NULL, + }; + + XVK_CHECK(vkCreatePipelineCache(vk_core.device, &pcci, NULL, &g_pipeline_cache)); + + XRVkModule_OnInitEnd( g_module_pipeline ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_pipeline ); + + vkDestroyPipelineCache(vk_core.device, g_pipeline_cache, NULL); + + XRVkModule_OnShutdownEnd( g_module_pipeline ); +} diff --git a/ref/vk/vk_pipeline.h b/ref/vk/vk_pipeline.h index 8f48d3439..c46440c12 100644 --- a/ref/vk/vk_pipeline.h +++ b/ref/vk/vk_pipeline.h @@ -1,8 +1,14 @@ #pragma once #include "vk_core.h" +#include "vk_module.h" + #include "vk_buffer.h" +extern RVkModule g_module_pipeline; + +extern VkPipelineCache g_pipeline_cache; + VkShaderModule R_VkShaderLoadFromMem(const void *ptr, uint32_t size, const char *name); void R_VkShaderDestroy(VkShaderModule module); @@ -83,9 +89,3 @@ vk_pipeline_ray_t VK_PipelineRayTracingCreate(const vk_pipeline_ray_create_info_ struct vk_combuf_s; void VK_PipelineRayTracingTrace(struct vk_combuf_s *combuf, const vk_pipeline_ray_t *pipeline, uint32_t width, uint32_t height, int scope_id); void VK_PipelineRayTracingDestroy(vk_pipeline_ray_t* pipeline); - - -qboolean VK_PipelineInit( void ); -void VK_PipelineShutdown( void ); - -extern VkPipelineCache g_pipeline_cache; diff --git a/ref/vk/vk_ray_accel.c b/ref/vk/vk_ray_accel.c index e3a1739e1..86de43989 100644 --- a/ref/vk/vk_ray_accel.c +++ b/ref/vk/vk_ray_accel.c @@ -18,6 +18,34 @@ #define MODULE_NAME "accel" #define LOG_MODULE rt +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // Impl_Init: VK_BufferCreate + // Impl_Shutdown: VK_BufferDestroy + &g_module_buffer, + + // buildAccel: R_VkStagingCommit + // RT_VkAccelPrepareTlas: R_VkStagingLockForBuffer, R_VkStagingUnlock + &g_module_staging, + + // buildAccel: R_VkGpuScope_Register, R_VkCombufScopeBegin, R_VkCombufScopeEnd + &g_module_combuf, + + // createOrUpdateAccelerationStructure: R_GeometryBuffer_Get + // RT_BlasBuild: R_GeometryBuffer_Get + &g_module_geometry +}; + +RVkModule g_module_ray_accel = { + .name = "ray_accel", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct rt_blas_s { const char *debug_name; rt_blas_usage_e usage; @@ -365,54 +393,6 @@ vk_resource_t RT_VkAccelPrepareTlas(vk_combuf_t *combuf) { }; } -qboolean RT_VkAccelInit(void) { - if (!VK_BufferCreate("ray accels_buffer", &g_accel.accels_buffer, MAX_ACCELS_BUFFER, - VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT - )) - { - return false; - } - g_accel.accels_buffer_addr = R_VkBufferGetDeviceAddress(g_accel.accels_buffer.buffer); - - if (!VK_BufferCreate("ray scratch_buffer", &g_accel.scratch_buffer, MAX_SCRATCH_BUFFER, - VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT - )) { - return false; - } - g_accel.scratch_buffer_addr = R_VkBufferGetDeviceAddress(g_accel.scratch_buffer.buffer); - - // TODO this doesn't really need to be host visible, use staging - if (!VK_BufferCreate("ray tlas_geom_buffer", &g_accel.tlas_geom_buffer, sizeof(VkAccelerationStructureInstanceKHR) * MAX_INSTANCES * 2, - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { - // FIXME complain, handle - return false; - } - g_accel.tlas_geom_buffer_addr = R_VkBufferGetDeviceAddress(g_accel.tlas_geom_buffer.buffer); - R_FlippingBuffer_Init(&g_accel.tlas_geom_buffer_alloc, MAX_INSTANCES * 2); - - g_accel.accels_buffer_alloc = aloPoolCreate(MAX_ACCELS_BUFFER, MAX_INSTANCES, /* why */ 256); - - R_SPEEDS_COUNTER(g_accel.stats.instances_count, "instances", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_accel.stats.accels_built, "built", kSpeedsMetricCount); - - return true; -} - -void RT_VkAccelShutdown(void) { - if (g_accel.tlas != VK_NULL_HANDLE) - vkDestroyAccelerationStructureKHR(vk_core.device, g_accel.tlas, NULL); - - VK_BufferDestroy(&g_accel.scratch_buffer); - VK_BufferDestroy(&g_accel.accels_buffer); - VK_BufferDestroy(&g_accel.tlas_geom_buffer); - if (g_accel.accels_buffer_alloc) - aloPoolDestroy(g_accel.accels_buffer_alloc); -} - void RT_VkAccelNewMap(void) { const int expected_accels = 512; // TODO actually get this from playing the game const int accels_alignment = 256; // TODO where does this come from? @@ -647,3 +627,73 @@ qboolean RT_BlasBuild(struct rt_blas_s *blas, const struct vk_render_geometry_s Mem_Free(build_ranges); return retval; } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_ray_accel ); + + if ( vk_core.rtx ) { + if (!VK_BufferCreate("ray accels_buffer", &g_accel.accels_buffer, MAX_ACCELS_BUFFER, + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) + { + ERR( "Couldn't create Ray accelerations buffer." ); + g_module_ray_accel.state = RVkModuleState_NotInitialized; + return false; + } + g_accel.accels_buffer_addr = R_VkBufferGetDeviceAddress(g_accel.accels_buffer.buffer); + + if (!VK_BufferCreate("ray scratch_buffer", &g_accel.scratch_buffer, MAX_SCRATCH_BUFFER, + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) + { + ERR( "Couldn't create Ray scratch buffer." ); + g_module_ray_accel.state = RVkModuleState_NotInitialized; + return false; + } + g_accel.scratch_buffer_addr = R_VkBufferGetDeviceAddress(g_accel.scratch_buffer.buffer); + + // TODO this doesn't really need to be host visible, use staging + if (!VK_BufferCreate("ray tlas_geom_buffer", &g_accel.tlas_geom_buffer, sizeof(VkAccelerationStructureInstanceKHR) * MAX_INSTANCES * 2, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + // FIXME complain, handle -- NOTE(nilsoncore): Is it done? + ERR( "Couldn't create Ray TLAS geometry buffer." ); + g_module_ray_accel.state = RVkModuleState_NotInitialized; + return false; + } + g_accel.tlas_geom_buffer_addr = R_VkBufferGetDeviceAddress(g_accel.tlas_geom_buffer.buffer); + R_FlippingBuffer_Init(&g_accel.tlas_geom_buffer_alloc, MAX_INSTANCES * 2); + + g_accel.accels_buffer_alloc = aloPoolCreate(MAX_ACCELS_BUFFER, MAX_INSTANCES, /* why */ 256); + + R_SPEEDS_COUNTER(g_accel.stats.instances_count, "instances", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_accel.stats.accels_built, "built", kSpeedsMetricCount); + + gEngine.Con_Printf( S_NOTE "Module '%s' is initialized with enabled functionality.\n", g_module_ray_accel.name ); + } else { + gEngine.Con_Printf( S_WARN "Module '%s' is initialized with disabled functionality.\n", g_module_ray_accel.name ); + gEngine.Con_Printf( S_WARN "Switch to Ray Tracing renderer to enable it. (It must be supported on your graphics device)\n" ); + } + + XRVkModule_OnInitEnd( g_module_ray_accel ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_ray_accel ); + + if ( vk_core.rtx ) { + if (g_accel.tlas != VK_NULL_HANDLE) + vkDestroyAccelerationStructureKHR(vk_core.device, g_accel.tlas, NULL); + + VK_BufferDestroy(&g_accel.scratch_buffer); + VK_BufferDestroy(&g_accel.accels_buffer); + VK_BufferDestroy(&g_accel.tlas_geom_buffer); + if (g_accel.accels_buffer_alloc) + aloPoolDestroy(g_accel.accels_buffer_alloc); + } + + XRVkModule_OnShutdownEnd( g_module_ray_accel ); +} diff --git a/ref/vk/vk_ray_accel.h b/ref/vk/vk_ray_accel.h index dbaa3fa6c..237927b0f 100644 --- a/ref/vk/vk_ray_accel.h +++ b/ref/vk/vk_ray_accel.h @@ -1,12 +1,13 @@ #pragma once #include "vk_core.h" +#include "vk_module.h" + #include "vk_buffer.h" #include "vk_math.h" #include "ray_resources.h" -qboolean RT_VkAccelInit(void); -void RT_VkAccelShutdown(void); +extern RVkModule g_module_ray_accel; void RT_VkAccelNewMap(void); void RT_VkAccelFrameBegin(void); diff --git a/ref/vk/vk_ray_internal.h b/ref/vk/vk_ray_internal.h index e191b2cc5..ef21cf4fc 100644 --- a/ref/vk/vk_ray_internal.h +++ b/ref/vk/vk_ray_internal.h @@ -1,6 +1,8 @@ #pragma once #include "vk_core.h" +#include "vk_module.h" + #include "vk_buffer.h" #include "vk_const.h" #include "vk_rtx.h" @@ -11,6 +13,8 @@ #include "shaders/ray_interop.h" +extern RVkModule g_module_ray_model; + typedef struct Kusok vk_kusok_data_t; typedef struct rt_draw_instance_s { @@ -104,7 +108,4 @@ void RT_KusochkiFree(const rt_kusochki_t*); //struct vk_render_geometry_s; //qboolean RT_KusochkiUpload(uint32_t kusochki_offset, const struct vk_render_geometry_s *geoms, int geoms_count, int override_texture_id, const vec4_t *override_color); -qboolean RT_DynamicModelInit(void); -void RT_DynamicModelShutdown(void); - void RT_DynamicModelProcessFrame(void); diff --git a/ref/vk/vk_ray_model.c b/ref/vk/vk_ray_model.c index 2fb1fb34a..4083d5b66 100644 --- a/ref/vk/vk_ray_model.c +++ b/ref/vk/vk_ray_model.c @@ -12,11 +12,37 @@ #include "vk_logs.h" #include "profiler.h" +#include "vk_ray_accel.h" // Blas + #include "eiface.h" #include "xash3d_mathlib.h" #include +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // RT_KusochkiUpload: R_VkStagingLockForBuffer, R_VkStagingUnlock + &g_module_staging, + + // RT_ModelCreate: RT_BlasCreate, RT_BlasBuild, RT_BlasGetDeviceAddress, RT_BlasDestroy + // RT_ModelDestroy: RT_BlasDestroy + // RT_ModelUpdate: RT_BlasBuild + // RT_DynamicModelProcessFrame: RT_BlasBuild + // Impl_Init: RT_BlasCreate, RT_BlasPreallocate, RT_BlasGetDeviceAddress + // Impl_Shutdown: RT_BlasDestroy + &g_module_ray_accel +}; + +RVkModule g_module_ray_model = { + .name = "ray_model", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + xvk_ray_model_state_t g_ray_model_state; typedef struct rt_model_s { @@ -389,38 +415,6 @@ static struct { rt_dynamic_t groups[MATERIAL_MODE_COUNT]; } g_dyn; -qboolean RT_DynamicModelInit(void) { - for (int i = 0; i < MATERIAL_MODE_COUNT; ++i) { - struct rt_blas_s *blas = RT_BlasCreate(group_names[i], kBlasBuildDynamicFast); - if (!blas) { - // FIXME destroy allocated - gEngine.Con_Printf(S_ERROR "Couldn't create blas for %s\n", group_names[i]); - return false; - } - - if (!RT_BlasPreallocate(blas, (rt_blas_preallocate_t){ - // TODO better estimates for these constants - .max_geometries = MAX_RT_DYNAMIC_GEOMETRIES, - .max_prims_per_geometry = 256, - .max_vertex_per_geometry = 256, - })) { - // FIXME destroy allocated - gEngine.Con_Printf(S_ERROR "Couldn't preallocate blas for %s\n", group_names[i]); - return false; - } - g_dyn.groups[i].blas = blas; - g_dyn.groups[i].blas_addr = RT_BlasGetDeviceAddress(blas); - } - - return true; -} - -void RT_DynamicModelShutdown(void) { - for (int i = 0; i < MATERIAL_MODE_COUNT; ++i) { - RT_BlasDestroy(g_dyn.groups[i].blas); - } -} - void RT_DynamicModelProcessFrame(void) { APROF_SCOPE_DECLARE_BEGIN(process, __FUNCTION__); for (int i = 0; i < MATERIAL_MODE_COUNT; ++i) { @@ -479,3 +473,52 @@ void RT_FrameAddOnce( rt_frame_add_once_t args ) { } } +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_ray_model ); + + if ( vk_core.rtx ) { + for (int i = 0; i < MATERIAL_MODE_COUNT; ++i) { + struct rt_blas_s *blas = RT_BlasCreate(group_names[i], kBlasBuildDynamicFast); + if (!blas) { + // FIXME destroy allocated + gEngine.Con_Printf(S_ERROR "Couldn't create BLAS for %s\n", group_names[i]); + g_module_ray_model.state = RVkModuleState_NotInitialized; + return false; + } + + if (!RT_BlasPreallocate(blas, (rt_blas_preallocate_t){ + // TODO better estimates for these constants + .max_geometries = MAX_RT_DYNAMIC_GEOMETRIES, + .max_prims_per_geometry = 256, + .max_vertex_per_geometry = 256, + })) { + // FIXME destroy allocated + gEngine.Con_Printf(S_ERROR "Couldn't preallocate BLAS for %s\n", group_names[i]); + g_module_ray_model.state = RVkModuleState_NotInitialized; + return false; + } + g_dyn.groups[i].blas = blas; + g_dyn.groups[i].blas_addr = RT_BlasGetDeviceAddress(blas); + } + + gEngine.Con_Printf( S_NOTE "Module '%s' is initialized with enabled functionality.\n", g_module_ray_model.name ); + } else { + gEngine.Con_Printf( S_WARN "Module '%s' is initialized with disabled functionality.\n", g_module_ray_model.name ); + gEngine.Con_Printf( S_WARN "Switch to Ray Tracing renderer to enable it. (It must be supported on your graphics device)\n" ); + } + + XRVkModule_OnInitEnd( g_module_ray_model ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_ray_model ); + + if ( vk_core.rtx ) { + for (int i = 0; i < MATERIAL_MODE_COUNT; ++i) { + RT_BlasDestroy(g_dyn.groups[i].blas); + } + } + + XRVkModule_OnShutdownEnd( g_module_ray_model ); +} diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index a6c3c6693..77f08df5a 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -36,6 +36,48 @@ PROFILER_SCOPES(SCOPE_DECLARE) #undef SCOPE_DECLARE +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // Impl_Init: VK_BufferCreate + // Impl_Shutdown: VK_BufferDestroy + &g_module_buffer, + + // createPipelines: VK_PipelineGraphicsCreate + &g_module_pipeline, + + // VK_RenderBegin: VK_RayFrameBegin + // VK_RenderEndRTX: VK_RayFrameEnd + &g_module_rtx, + + // R_RenderModelCreate: RT_ModelCreate + // R_RenderModelDestroy: RT_ModelDestroy + // R_RenderModelUpdate: RT_ModelUpdate + // R_RenderModelUpdateMaterials: RT_ModelUpdateMaterials + // R_RenderModelDraw: RT_FrameAddModel + // R_RenderDrawOnce: RT_FrameAddOnce + // &g_module_ray_model, -- NOTE(nilsoncore): For some reason we don't see this module although we use its functions?? Whatever, it's inside rtx anyway. + + // VK_Render_FIXME_Barrier: R_GeometryBuffer_Get + // VK_RenderEnd: R_GeometryBuffer_Get + // VK_RenderEndRTX: R_GeometryBuffer_Get + // R_RenderDrawOnce: R_GeometryBufferAllocOnceAndLock, R_GeometryBufferUnlock + &g_module_geometry, + + // VK_RenderEnd: R_VkTextureGetDescriptorUnorm + &g_module_textures_api, // It has to be loaded first + // &g_module_textures -- @TexturesInitHardcode +}; + +RVkModule g_module_render = { + .name = "render", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { matrix4x4 mvp; vec4_t color; @@ -294,70 +336,6 @@ static struct { qboolean current_frame_is_ray_traced; } g_render_state; -qboolean VK_RenderInit( void ) { - PROFILER_SCOPES(APROF_SCOPE_INIT); - - g_render.use_material_textures = gEngine.Cvar_Get( "vk_use_material_textures", "0", FCVAR_GLCONFIG, "Use PBR material textures for traditional rendering too" ); - - g_render.ubo_align = Q_max(4, vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment); - - const uint32_t uniform_unit_size = ((sizeof(uniform_data_t) + g_render.ubo_align - 1) / g_render.ubo_align) * g_render.ubo_align; - const uint32_t uniform_buffer_size = uniform_unit_size * MAX_UNIFORM_SLOTS; - R_FlippingBuffer_Init(&g_render_state.uniform_alloc, uniform_buffer_size); - - if (!VK_BufferCreate("render uniform_buffer", &g_render.uniform_buffer, uniform_buffer_size, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | (vk_core.rtx ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0))) - return false; - - { - VkDescriptorBufferInfo dbi_uniform_data = { - .buffer = g_render.uniform_buffer.buffer, - .offset = 0, - .range = sizeof(uniform_data_t), - }; - VkDescriptorBufferInfo dbi_uniform_lights = { - .buffer = g_render.uniform_buffer.buffer, - .offset = 0, - .range = sizeof(vk_ubo_lights_t), - }; - VkWriteDescriptorSet wds[] = {{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - .pBufferInfo = &dbi_uniform_data, - .dstSet = vk_desc_fixme.ubo_sets[0], // FIXME - }, { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - .pBufferInfo = &dbi_uniform_lights, - .dstSet = vk_desc_fixme.ubo_sets[1], // FIXME - }}; - vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL); - } - - if (!createPipelines()) - return false; - - R_SPEEDS_COUNTER(g_render.stats.dynamic_model_count, "models_dynamic", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_render.stats.models_count, "models", kSpeedsMetricCount); - return true; -} - -void VK_RenderShutdown( void ) -{ - for (int i = 0; i < ARRAYSIZE(g_render.pipelines); ++i) - vkDestroyPipeline(vk_core.device, g_render.pipelines[i], NULL); - vkDestroyPipelineLayout( vk_core.device, g_render.pipeline_layout, NULL ); - - VK_BufferDestroy( &g_render.uniform_buffer ); -} - enum { UNIFORM_UNSET = 0, UNIFORM_UPLOADED = 16, @@ -862,3 +840,81 @@ void R_RenderDrawOnce(r_draw_once_t args) { g_render.stats.dynamic_model_count++; } + +static qboolean Impl_Init( void ) { + PROFILER_SCOPES(APROF_SCOPE_INIT); + + XRVkModule_OnInitStart( g_module_render ); + + g_render.use_material_textures = gEngine.Cvar_Get( "vk_use_material_textures", "0", FCVAR_GLCONFIG, "Use PBR material textures for traditional rendering too" ); + + g_render.ubo_align = Q_max(4, vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment); + + const uint32_t uniform_unit_size = ((sizeof(uniform_data_t) + g_render.ubo_align - 1) / g_render.ubo_align) * g_render.ubo_align; + const uint32_t uniform_buffer_size = uniform_unit_size * MAX_UNIFORM_SLOTS; + R_FlippingBuffer_Init(&g_render_state.uniform_alloc, uniform_buffer_size); + + if (!VK_BufferCreate("render uniform_buffer", &g_render.uniform_buffer, uniform_buffer_size, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | (vk_core.rtx ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0))) { + gEngine.Con_Printf( S_ERROR "Couldn't create uniform render buffer.\n" ); + g_module_render.state = RVkModuleState_NotInitialized; + return false; + } + + { + VkDescriptorBufferInfo dbi_uniform_data = { + .buffer = g_render.uniform_buffer.buffer, + .offset = 0, + .range = sizeof(uniform_data_t), + }; + VkDescriptorBufferInfo dbi_uniform_lights = { + .buffer = g_render.uniform_buffer.buffer, + .offset = 0, + .range = sizeof(vk_ubo_lights_t), + }; + VkWriteDescriptorSet wds[] = {{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .pBufferInfo = &dbi_uniform_data, + .dstSet = vk_desc_fixme.ubo_sets[0], // FIXME + }, { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .pBufferInfo = &dbi_uniform_lights, + .dstSet = vk_desc_fixme.ubo_sets[1], // FIXME + }}; + vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL); + } + + if (!createPipelines()) { + gEngine.Con_Printf( S_ERROR "Couldn't create render pipelines.\n" ); + g_module_render.state = RVkModuleState_NotInitialized; + return false; + } + + R_SPEEDS_COUNTER(g_render.stats.dynamic_model_count, "models_dynamic", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_render.stats.models_count, "models", kSpeedsMetricCount); + + XRVkModule_OnInitEnd( g_module_render ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_render ); + + for (int i = 0; i < ARRAYSIZE(g_render.pipelines); ++i) + vkDestroyPipeline(vk_core.device, g_render.pipelines[i], NULL); + + vkDestroyPipelineLayout( vk_core.device, g_render.pipeline_layout, NULL ); + + VK_BufferDestroy( &g_render.uniform_buffer ); + + XRVkModule_OnShutdownEnd( g_module_render ); +} diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index afef8b2bb..63209d3cb 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -1,11 +1,13 @@ #pragma once + +#include "vk_core.h" +#include "vk_module.h" + #include "vk_materials.h" #include "vk_common.h" #include "vk_const.h" -#include "vk_core.h" -qboolean VK_RenderInit( void ); -void VK_RenderShutdown( void ); +extern RVkModule g_module_render; struct ref_viewpass_s; void VK_RenderSetupCamera( const struct ref_viewpass_s *rvp ); diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index 49e07ffa2..a948c0b1f 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -56,6 +56,50 @@ enum { #define MAX_RESOURCES 32 +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // Impl_Init: VK_BufferCreate + // Impl_Shutdown: VK_BufferDestroy + &g_module_buffer, + + // VK_RayNewMapBegin: RT_VkAccelNewMap + // VK_RayFrameBegin: RT_VkAccelFrameBegin + // performTracing: RT_VkAccelPrepareTlas + &g_module_ray_accel, + + // VK_RayNewMapBegin: RT_RayModel_Clear + // VK_RayFrameBegin: XVK_RayModel_ClearForNextFrame + // VK_RayFrameEnd: RT_DynamicModelProcessFrame + // Impl_Init: RT_RayModel_Clear + &g_module_ray_model, + + // VK_RayNewMapEnd: R_VkTexturesGetSkyboxDescriptorImageInfo, R_VkTexturesGetBlueNoiseImageInfo + // prepareUniformBuffer: R_TexturesGetSkyboxInfo + // Impl_Init: R_VkTexturesGetAllDescriptorsArray + &g_module_textures_api, + // &g_module_textures, -- @TexturesInitHardcode + + // VK_RayFrameBegin: RT_LightsFrameBegin + // VK_RayFrameEnd: RT_LightsFrameEnd, VK_LightsUpload + &g_module_light, + + // performTracing: R_VkImageBlit, R_VkImageClear + // cleanupResources: R_VkImageDestroy + // reloadMainpipe: R_VkImageDestroy, R_VkImageCreate + // VK_RayFrameEnd: R_VkImageClear, R_VkImageBlit + &g_module_image +}; + +RVkModule g_module_rtx = { + .name = "rtx", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { char name[64]; vk_resource_t resource; @@ -304,6 +348,7 @@ static void performTracing( vk_combuf_t *combuf, const perform_tracing_args_t* a ASSERT(res->source_index_plus_1 <= COUNTOF(g_rtx.res)); rt_resource_t *const src = g_rtx.res + res->source_index_plus_1 - 1; + ASSERT(res != src); // Swap resources const vk_resource_t tmp_res = res->resource; @@ -319,6 +364,7 @@ static void performTracing( vk_combuf_t *combuf, const perform_tracing_args_t* a // If there was no initial state, prepare it. (this should happen only for the first frame) if (g_rtx.discontinuity || res->resource.write.pipelines == 0) { // TODO is there a better way? Can image be cleared w/o explicit clear op? + DEBUG("discontinuity: %s", res->name); R_VkImageClear( cmdbuf, res->image.image ); res->resource.write.pipelines = VK_PIPELINE_STAGE_TRANSFER_BIT; res->resource.write.image_layout = VK_IMAGE_LAYOUT_GENERAL; @@ -326,6 +372,10 @@ static void performTracing( vk_combuf_t *combuf, const perform_tracing_args_t* a } } + if (g_rtx.discontinuity) + DEBUG("discontinuity => false"); + g_rtx.discontinuity = false; + // Clear intra-frame resources for (int i = ExternalResource_COUNT; i < MAX_RESOURCES; ++i) { rt_resource_t* const res = g_rtx.res + i; @@ -688,97 +738,110 @@ void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args) tail: APROF_SCOPE_END(ray_frame_end); - g_rtx.discontinuity = false; } static void reloadPipeline( void ) { g_rtx.reload_pipeline = true; } -qboolean VK_RayInit( void ) -{ - ASSERT(vk_core.rtx); - // TODO complain and cleanup on failure +void RT_FrameDiscontinuity( void ) { + DEBUG("%s", __FUNCTION__); + g_rtx.discontinuity = true; +} - g_rtx.max_frame_width = MIN_FRAME_WIDTH; - g_rtx.max_frame_height = MIN_FRAME_HEIGHT; +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_rtx ); - if (!RT_VkAccelInit()) - return false; + // TODO complain and cleanup on failure + if ( vk_core.rtx ) { - // FIXME shutdown accel - if (!RT_DynamicModelInit()) - return false; + g_rtx.max_frame_width = MIN_FRAME_WIDTH; + g_rtx.max_frame_height = MIN_FRAME_HEIGHT; #define REGISTER_EXTERNAL(type, name_) \ - Q_strncpy(g_rtx.res[ExternalResource_##name_].name, #name_, sizeof(g_rtx.res[0].name)); \ - g_rtx.res[ExternalResource_##name_].refcount = 1; - EXTERNAL_RESOUCES(REGISTER_EXTERNAL) + Q_strncpy(g_rtx.res[ExternalResource_##name_].name, #name_, sizeof(g_rtx.res[0].name)); \ + g_rtx.res[ExternalResource_##name_].refcount = 1; + EXTERNAL_RESOUCES(REGISTER_EXTERNAL) #undef REGISTER_EXTERNAL - g_rtx.res[ExternalResource_textures].resource = (vk_resource_t){ - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .value = (vk_descriptor_value_t){ - .image_array = R_VkTexturesGetAllDescriptorsArray(), - } - }; - g_rtx.res[ExternalResource_textures].refcount = 1; + g_rtx.res[ExternalResource_textures].resource = (vk_resource_t){ + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .value = (vk_descriptor_value_t){ + .image_array = R_VkTexturesGetAllDescriptorsArray(), + } + }; + g_rtx.res[ExternalResource_textures].refcount = 1; - reloadMainpipe(); - if (!g_rtx.mainpipe) - return false; + reloadMainpipe(); + if (!g_rtx.mainpipe) { + ERR( "Couldn't reload Ray Tracing mainpipe." ); + g_module_rtx.state = RVkModuleState_NotInitialized; + return false; + } - g_rtx.uniform_unit_size = ALIGN_UP(sizeof(struct UniformBuffer), vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment); + g_rtx.uniform_unit_size = ALIGN_UP(sizeof(struct UniformBuffer), vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment); - if (!VK_BufferCreate("ray uniform_buffer", &g_rtx.uniform_buffer, g_rtx.uniform_unit_size * MAX_FRAMES_IN_FLIGHT, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) - { - return false; - } + if (!VK_BufferCreate("ray uniform_buffer", &g_rtx.uniform_buffer, g_rtx.uniform_unit_size * MAX_FRAMES_IN_FLIGHT, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) + { + ERR( "Couldn't create Ray Tracing uniform buffer." ); + g_module_rtx.state = RVkModuleState_NotInitialized; + return false; + } - if (!VK_BufferCreate("ray kusochki_buffer", &g_ray_model_state.kusochki_buffer, sizeof(vk_kusok_data_t) * MAX_KUSOCHKI, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { - // FIXME complain, handle - return false; - } + if (!VK_BufferCreate("ray kusochki_buffer", &g_ray_model_state.kusochki_buffer, sizeof(vk_kusok_data_t) * MAX_KUSOCHKI, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) + { + // FIXME complain, handle + ERR( "Couldn't create Ray Tracing kusochki buffer." ); + g_module_rtx.state = RVkModuleState_NotInitialized; + return false; + } - if (!VK_BufferCreate("model headers", &g_ray_model_state.model_headers_buffer, sizeof(struct ModelHeader) * MAX_INSTANCES, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { - // FIXME complain, handle - return false; - } + if (!VK_BufferCreate("model headers", &g_ray_model_state.model_headers_buffer, sizeof(struct ModelHeader) * MAX_INSTANCES, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) + { + // FIXME complain, handle + ERR( "Couldn't create Ray Tracing model headers buffer." ); + g_module_rtx.state = RVkModuleState_NotInitialized; + return false; + } - RT_RayModel_Clear(); + RT_RayModel_Clear(); - gEngine.Cmd_AddCommand("rt_debug_reload_pipelines", reloadPipeline, "Reload RT pipelines"); + gEngine.Cmd_AddCommand("rt_debug_reload_pipelines", reloadPipeline, "Reload RT pipelines"); #define X(name) #name ", " - g_rtx.debug.rt_debug_display_only = gEngine.Cvar_Get("rt_debug_display_only", "", FCVAR_GLCONFIG, - "Display only the specified channel (" LIST_DISPLAYS(X) "etc)"); + g_rtx.debug.rt_debug_display_only = gEngine.Cvar_Get("rt_debug_display_only", "", FCVAR_GLCONFIG, + "Display only the specified channel (" LIST_DISPLAYS(X) "etc)"); #undef X - g_rtx.debug.rt_debug_fixed_random_seed = gEngine.Cvar_Get("rt_debug_fixed_random_seed", "", FCVAR_GLCONFIG, - "Fix random seed value for RT monte carlo sampling. Used for reproducible regression testing"); + g_rtx.debug.rt_debug_fixed_random_seed = gEngine.Cvar_Get("rt_debug_fixed_random_seed", "", FCVAR_GLCONFIG, + "Fix random seed value for RT monte carlo sampling. Used for reproducible regression testing"); + gEngine.Con_Printf( S_NOTE "Module '%s' is initialized with enabled functionality.\n", g_module_rtx.name ); + } else { + gEngine.Con_Printf( S_WARN "Module '%s' is initialized with disabled functionality.\n", g_module_rtx.name ); + gEngine.Con_Printf( S_WARN "Switch to Ray Tracing renderer to enable it. (It must be supported on your graphics device)\n" ); + } + + XRVkModule_OnInitEnd( g_module_rtx ); return true; } -void VK_RayShutdown( void ) { - ASSERT(vk_core.rtx); - - destroyMainpipe(); - - VK_BufferDestroy(&g_ray_model_state.model_headers_buffer); - VK_BufferDestroy(&g_ray_model_state.kusochki_buffer); - VK_BufferDestroy(&g_rtx.uniform_buffer); +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_rtx ); + + if ( vk_core.rtx ) { + destroyMainpipe(); - RT_VkAccelShutdown(); - RT_DynamicModelShutdown(); -} + VK_BufferDestroy(&g_ray_model_state.model_headers_buffer); + VK_BufferDestroy(&g_ray_model_state.kusochki_buffer); + VK_BufferDestroy(&g_rtx.uniform_buffer); + } -void RT_FrameDiscontinuity( void ) { - g_rtx.discontinuity = true; -} + XRVkModule_OnShutdownEnd( g_module_rtx ); +} \ No newline at end of file diff --git a/ref/vk/vk_rtx.h b/ref/vk/vk_rtx.h index a009e7015..c34e651f2 100644 --- a/ref/vk/vk_rtx.h +++ b/ref/vk/vk_rtx.h @@ -1,7 +1,11 @@ #pragma once -#include "vk_geometry.h" #include "vk_core.h" +#include "vk_module.h" + +#include "vk_geometry.h" + +extern RVkModule g_module_rtx; void VK_RayFrameBegin( void ); @@ -30,9 +34,6 @@ void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args); void VK_RayNewMapBegin( void ); void VK_RayNewMapEnd( void ); -qboolean VK_RayInit( void ); -void VK_RayShutdown( void ); - struct vk_render_geometry_s; struct rt_model_s; struct r_vk_material_s; diff --git a/ref/vk/vk_scene.c b/ref/vk/vk_scene.c index 766824c2d..83e8a90d6 100644 --- a/ref/vk/vk_scene.c +++ b/ref/vk/vk_scene.c @@ -45,6 +45,69 @@ PROFILER_SCOPES(SCOPE_DECLARE) #undef SCOPE_DECLARE +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // loadLights: RT_LightsLoadBegin, RT_LightsLoadEnd + // loadMap: RT_LightsNewMap + // VK_SceneRender: RT_LightAddFlashlight + &g_module_light, + + // loadLights: RT_LightsLoadBegin + // preloadModels: R_BrushModelLoad + // reloadPatches: R_BrushModelDestroyAll + // R_SceneMapDestroy: R_BrushModelDestroyAll + // drawEntity: R_BrushModelDraw + // VK_SceneRender: R_BrushModelDraw + &g_module_brush, + + // preloadModels: R_VkMaterialsLoadForModel + // loadMap: R_VkMaterialsReload + &g_module_materials, + + // preloadModels: R_StudioModelPreload -- studio_model module + // loadMap: R_StudioCacheClear + // R_NewMap: R_StudioResetPlayerModels + // drawEntity: VK_StudioDrawModel + // VK_SceneRender: R_RunViewmodelEvents, R_DrawViewModel + &g_module_studio, + + // loadMap: R_GeometryBuffer_MapClear -- obsolete, doesn't do anything + &g_module_geometry, + + // loadMap: VK_RayNewMapBegin, VK_RayNewMapEnd + // R_NewMap: RT_FrameDiscontinuity + &g_module_rtx, + + // loadMap: R_SpriteNewMapFIXME + // drawEntity: R_VkSpriteDrawModel + &g_module_sprite, + + // loadMap: R_TextureSetupSky + &g_module_textures_api, + + // reloadPatches: R_VkStagingFlushSync + &g_module_staging, + + // VK_SceneRender: VK_RenderSetupCamera, VK_RenderDebugLabelBegin, VK_RenderDebugLabelEnd + &g_module_render, + + // CL_DrawBeams: R_BeamDrawCustomEntity, R_BeamDraw + &g_module_beams + + // loadMap: VK_ClearLightmap, VK_RunLightStyles, VK_UploadLightmap -- make lightmap module? + // loadMap: XVK_ParseMapEntities, XVK_ParseMapPatches -- make mapents module? +}; + +RVkModule g_module_scene = { + .name = "scene", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct vk_trans_entity_s { struct cl_entity_s *entity; int render_mode; @@ -186,18 +249,6 @@ static void reloadPatches( void ) { R_VkStagingFlushSync(); } -void VK_SceneInit( void ) -{ - PROFILER_SCOPES(APROF_SCOPE_INIT); - - g_lists.draw_list = g_lists.draw_stack; - g_lists.draw_stack_pos = 0; - - if (vk_core.rtx) { - gEngine.Cmd_AddCommand("rt_debug_reload_patches", reloadPatches, "Reload patched entities, lights and extra PBR materials"); - } -} - #define R_ModelOpaque( rm ) ( rm == kRenderNormal ) int R_FIXME_GetEntityRenderMode( cl_entity_t *ent ) { @@ -466,123 +517,6 @@ static int R_TransEntityCompare( const void *a, const void *b) return 0; } -// FIXME where should this function be -#define RP_NORMALPASS() true // FIXME ??? -int CL_FxBlend( cl_entity_t *e ) // FIXME do R_SetupFrustum: , vec3_t vforward ) -{ - int blend = 0; - float offset, dist; - vec3_t tmp; - - offset = ((int)e->index ) * 363.0f; // Use ent index to de-sync these fx - - switch( e->curstate.renderfx ) - { - case kRenderFxPulseSlowWide: - blend = e->curstate.renderamt + 0x40 * sin( gpGlobals->time * 2 + offset ); - break; - case kRenderFxPulseFastWide: - blend = e->curstate.renderamt + 0x40 * sin( gpGlobals->time * 8 + offset ); - break; - case kRenderFxPulseSlow: - blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->time * 2 + offset ); - break; - case kRenderFxPulseFast: - blend = e->curstate.renderamt + 0x10 * sin( gpGlobals->time * 8 + offset ); - break; - case kRenderFxFadeSlow: - if( RP_NORMALPASS( )) - { - if( e->curstate.renderamt > 0 ) - e->curstate.renderamt -= 1; - else e->curstate.renderamt = 0; - } - blend = e->curstate.renderamt; - break; - case kRenderFxFadeFast: - if( RP_NORMALPASS( )) - { - if( e->curstate.renderamt > 3 ) - e->curstate.renderamt -= 4; - else e->curstate.renderamt = 0; - } - blend = e->curstate.renderamt; - break; - case kRenderFxSolidSlow: - if( RP_NORMALPASS( )) - { - if( e->curstate.renderamt < 255 ) - e->curstate.renderamt += 1; - else e->curstate.renderamt = 255; - } - blend = e->curstate.renderamt; - break; - case kRenderFxSolidFast: - if( RP_NORMALPASS( )) - { - if( e->curstate.renderamt < 252 ) - e->curstate.renderamt += 4; - else e->curstate.renderamt = 255; - } - blend = e->curstate.renderamt; - break; - case kRenderFxStrobeSlow: - blend = 20 * sin( gpGlobals->time * 4 + offset ); - if( blend < 0 ) blend = 0; - else blend = e->curstate.renderamt; - break; - case kRenderFxStrobeFast: - blend = 20 * sin( gpGlobals->time * 16 + offset ); - if( blend < 0 ) blend = 0; - else blend = e->curstate.renderamt; - break; - case kRenderFxStrobeFaster: - blend = 20 * sin( gpGlobals->time * 36 + offset ); - if( blend < 0 ) blend = 0; - else blend = e->curstate.renderamt; - break; - case kRenderFxFlickerSlow: - blend = 20 * (sin( gpGlobals->time * 2 ) + sin( gpGlobals->time * 17 + offset )); - if( blend < 0 ) blend = 0; - else blend = e->curstate.renderamt; - break; - case kRenderFxFlickerFast: - blend = 20 * (sin( gpGlobals->time * 16 ) + sin( gpGlobals->time * 23 + offset )); - if( blend < 0 ) blend = 0; - else blend = e->curstate.renderamt; - break; - case kRenderFxHologram: - case kRenderFxDistort: - VectorCopy( e->origin, tmp ); - VectorSubtract( tmp, g_camera.vieworg, tmp ); - dist = DotProduct( tmp, g_camera.vforward ); - - // turn off distance fade - if( e->curstate.renderfx == kRenderFxDistort ) - dist = 1; - - if( dist <= 0 ) - { - blend = 0; - } - else - { - e->curstate.renderamt = 180; - if( dist <= 100 ) blend = e->curstate.renderamt; - else blend = (int) ((1.0f - ( dist - 100 ) * ( 1.0f / 400.0f )) * e->curstate.renderamt ); - blend += gEngine.COM_RandomLong( -32, 31 ); - } - break; - default: - blend = e->curstate.renderamt; - break; - } - - blend = bound( 0, blend, 255 ); - - return blend; -} - static void drawEntity( cl_entity_t *ent, int render_mode ) { const model_t *mod = ent->model; @@ -786,3 +720,27 @@ void CL_DrawBeams( int fTrans, BEAM *active_beams ) R_BeamDraw( pBeam, g_frametime ); } } + +static qboolean Impl_Init( void ) { + PROFILER_SCOPES(APROF_SCOPE_INIT); + + XRVkModule_OnInitStart( g_module_scene ); + + g_lists.draw_list = g_lists.draw_stack; + g_lists.draw_stack_pos = 0; + + if (vk_core.rtx) { + gEngine.Cmd_AddCommand("rt_debug_reload_patches", reloadPatches, "Reload patched entities, lights and extra PBR materials"); + } + + XRVkModule_OnInitEnd( g_module_scene ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_scene ); + + // Nothing to clear for now. + + XRVkModule_OnShutdownEnd( g_module_scene ); +} diff --git a/ref/vk/vk_scene.h b/ref/vk/vk_scene.h index b7dda8420..ad1fc027b 100644 --- a/ref/vk/vk_scene.h +++ b/ref/vk/vk_scene.h @@ -1,4 +1,6 @@ #pragma once + +#include "vk_module.h" #include "vk_const.h" #include "xash3d_types.h" @@ -6,11 +8,11 @@ #include "com_model.h" #include "ref_params.h" +extern RVkModule g_module_scene; + struct ref_viewpass_s; struct cl_entity_s; -void VK_SceneInit( void ); - void VK_SceneRender( const struct ref_viewpass_s *rvp ); qboolean R_AddEntity( struct cl_entity_s *clent, int type ); @@ -27,8 +29,6 @@ void R_RenderScene( void ); int R_WorldToScreen( const vec3_t point, vec3_t screen ); int TriWorldToScreen( const float *world, float *screen ); -// TODO should this be here? -int CL_FxBlend( struct cl_entity_s *e ); struct beam_s; void CL_DrawBeams( int fTrans, struct beam_s *active_beams ); void CL_AddCustomBeam( struct cl_entity_s *pEnvBeam ); diff --git a/ref/vk/vk_sprite.c b/ref/vk/vk_sprite.c index 8f582b006..22bfac9cf 100644 --- a/ref/vk/vk_sprite.c +++ b/ref/vk/vk_sprite.c @@ -14,6 +14,8 @@ #include "pmtrace.h" #include "pm_defs.h" +#include "vk_ray_internal.h" // @MaterialMode + #include #define MODULE_NAME "sprite" @@ -23,6 +25,40 @@ #define MAPSPRITE_SIZE 128 #define GLARE_FALLOFF 19000.0f +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // createQuadModel: R_GeometryRangeAlloc, R_GeometryRangeLock, R_GeometryRangeUnlock + // destroyQuadModel: R_GeometryRangeFree + &g_module_geometry, + + // createQuadModel: R_VkMaterialGetForTexture + // R_DrawSpriteQuad: R_VkMaterialGetForTexture + &g_module_materials, + + // createQuadModel: R_RenderModelCreate + // destroyQuadModel: R_RenderModelDestroy + // R_DrawSpriteQuad: R_RenderModelDraw + &g_module_render, + + // R_DrawSpriteQuad: R_VkMaterialModeFromRenderType -- @MaterialMode: Why is it in ray_model and not in render? + &g_module_ray_model, + + // VK_SpriteLoadFrame: R_TextureUploadFromFile + // Mod_LoadMapSprite: R_TextureUploadFromBuffer + // Mod_SpriteUnloadTextures: R_TextureFree + &g_module_textures_api +}; + +RVkModule g_module_sprite = { + .name = "sprite", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + static struct { struct { int sprites; @@ -129,17 +165,6 @@ static void destroyQuadModel(void) { g_sprite.quad.geom.block_handle.size = 0; } -qboolean R_SpriteInit(void) { - R_SPEEDS_COUNTER(g_sprite.stats.sprites, "count", kSpeedsMetricCount); - - return true; - // TODO return createQuadModel(); -} - -void R_SpriteShutdown(void) { - destroyQuadModel(); -} - void R_SpriteNewMapFIXME(void) { destroyQuadModel(); ASSERT(createQuadModel()); @@ -1109,3 +1134,21 @@ void Mod_SpriteUnloadTextures( void *data ) } } } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_sprite ); + + R_SPEEDS_COUNTER(g_sprite.stats.sprites, "count", kSpeedsMetricCount); + + XRVkModule_OnInitEnd( g_module_sprite ); + return true; + // TODO return createQuadModel(); +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_sprite ); + + destroyQuadModel(); + + XRVkModule_OnShutdownEnd( g_module_sprite ); +} diff --git a/ref/vk/vk_sprite.h b/ref/vk/vk_sprite.h index d63934428..485304453 100644 --- a/ref/vk/vk_sprite.h +++ b/ref/vk/vk_sprite.h @@ -1,7 +1,11 @@ #pragma once +#include "vk_module.h" + #include "vk_common.h" +extern RVkModule g_module_sprite; + void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite ); int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame ); void Mod_LoadMapSprite( struct model_s *mod, const void *buffer, size_t size, qboolean *loaded ); @@ -9,9 +13,6 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded, ui void R_VkSpriteDrawModel( cl_entity_t *e, float blend ); -qboolean R_SpriteInit(void); -void R_SpriteShutdown(void); - // FIXME needed to recreate the sprite quad model, otherwise its memory will be freed, reused and corrupted void R_SpriteNewMapFIXME(void); diff --git a/ref/vk/vk_staging.c b/ref/vk/vk_staging.c index b87f0f513..8b9c9f366 100644 --- a/ref/vk/vk_staging.c +++ b/ref/vk/vk_staging.c @@ -15,6 +15,34 @@ #define MAX_CONCURRENT_FRAMES 2 #define COMMAND_BUFFER_COUNT (MAX_CONCURRENT_FRAMES + 1) // to accommodate two frames in flight plus something trying to upload data before waiting for the next frame to complete +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // Impl_Init: R_VkCombufOpen, R_VkGpuScope_Register + // R_VkStagingFlushSync: R_VkCombufEnd + // commitBuffers: R_VkCombufScopeBegin, R_VkCombufScopeEnd + // commitImages: R_VkCombufScopeBegin, R_VkCombufScopeEnd + // getCurrentCombuf: R_VkCombufBegin + // R_VkStagingFrameEnd: R_VkCombufEnd + &g_module_combuf, + + // commitBuffers: DEBUG_NV_CHECKPOINTF + &g_module_nv_aftermath, + + // Impl_Init: VK_BufferCreate + // Impl_Shutdown: VK_BufferDestroy + &g_module_buffer +}; + +RVkModule g_module_staging = { + .name = "staging", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { VkImage image; VkImageLayout layout; @@ -54,33 +82,6 @@ static struct { int image_upload_scope_id; } g_staging = {0}; -qboolean R_VkStagingInit(void) { - if (!VK_BufferCreate("staging", &g_staging.buffer, DEFAULT_STAGING_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) - return false; - - g_staging.combuf[0] = R_VkCombufOpen(); - g_staging.combuf[1] = R_VkCombufOpen(); - g_staging.combuf[2] = R_VkCombufOpen(); - - R_FlippingBuffer_Init(&g_staging.buffer_alloc, DEFAULT_STAGING_SIZE); - - R_SPEEDS_COUNTER(g_staging.stats.total_size, "total_size", kSpeedsMetricBytes); - R_SPEEDS_COUNTER(g_staging.stats.buffers_size, "buffers_size", kSpeedsMetricBytes); - R_SPEEDS_COUNTER(g_staging.stats.images_size, "images_size", kSpeedsMetricBytes); - - R_SPEEDS_COUNTER(g_staging.stats.buffer_chunks, "buffer_chunks", kSpeedsMetricCount); - R_SPEEDS_COUNTER(g_staging.stats.images, "images", kSpeedsMetricCount); - - g_staging.buffer_upload_scope_id = R_VkGpuScope_Register("staging_buffers"); - g_staging.image_upload_scope_id = R_VkGpuScope_Register("staging_images"); - - return true; -} - -void R_VkStagingShutdown(void) { - VK_BufferDestroy(&g_staging.buffer); -} - // FIXME There's a severe race condition here. Submitting things manually and prematurely (before framectl had a chance to synchronize with the previous frame) // may lead to data races and memory corruption (e.g. writing into memory that's being read in some pipeline stage still going) void R_VkStagingFlushSync( void ) { @@ -309,3 +310,40 @@ vk_combuf_t *R_VkStagingFrameEnd(void) { return current; } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_staging ); + + if (!VK_BufferCreate("staging", &g_staging.buffer, DEFAULT_STAGING_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + gEngine.Con_Printf( S_ERROR "Couldn't create staging buffer.\n" ); + g_module_staging.state = RVkModuleState_NotInitialized; + return false; + } + + g_staging.combuf[0] = R_VkCombufOpen(); + g_staging.combuf[1] = R_VkCombufOpen(); + g_staging.combuf[2] = R_VkCombufOpen(); + + R_FlippingBuffer_Init(&g_staging.buffer_alloc, DEFAULT_STAGING_SIZE); + + R_SPEEDS_COUNTER(g_staging.stats.total_size, "total_size", kSpeedsMetricBytes); + R_SPEEDS_COUNTER(g_staging.stats.buffers_size, "buffers_size", kSpeedsMetricBytes); + R_SPEEDS_COUNTER(g_staging.stats.images_size, "images_size", kSpeedsMetricBytes); + + R_SPEEDS_COUNTER(g_staging.stats.buffer_chunks, "buffer_chunks", kSpeedsMetricCount); + R_SPEEDS_COUNTER(g_staging.stats.images, "images", kSpeedsMetricCount); + + g_staging.buffer_upload_scope_id = R_VkGpuScope_Register("staging_buffers"); + g_staging.image_upload_scope_id = R_VkGpuScope_Register("staging_images"); + + XRVkModule_OnInitEnd( g_module_staging ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_staging ); + + VK_BufferDestroy(&g_staging.buffer); + + XRVkModule_OnShutdownEnd( g_module_staging ); +} \ No newline at end of file diff --git a/ref/vk/vk_staging.h b/ref/vk/vk_staging.h index 2caa14230..6180fbba3 100644 --- a/ref/vk/vk_staging.h +++ b/ref/vk/vk_staging.h @@ -1,9 +1,9 @@ #pragma once #include "vk_core.h" +#include "vk_module.h" -qboolean R_VkStagingInit(void); -void R_VkStagingShutdown(void); +extern RVkModule g_module_staging; typedef int staging_handle_t; diff --git a/ref/vk/vk_studio.c b/ref/vk/vk_studio.c index 7091b8ae4..9e1fd8d08 100644 --- a/ref/vk/vk_studio.c +++ b/ref/vk/vk_studio.c @@ -38,6 +38,41 @@ #define ENGINE_GET_PARM( parm ) ENGINE_GET_PARM_( ( parm ), 0 ) #define CL_IsViewEntityLocalPlayer() ( ENGINE_GET_PARM( PARM_VIEWENT_INDEX ) == ENGINE_GET_PARM( PARM_PLAYER_INDEX ) ) +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // R_StudioDrawPoints: studioSubmodelRenderModelAcquire, studioSubmodelRenderModelRelease + // studioEntityModelDestroy: studioSubmodelRenderModelRelease + // studioEntityModelCreate: getStudioModelInfo + &g_module_studio_model, + + // buildStudioSubmodelGeometry: R_GeometryRangeLock, R_GeometryRangeUnlock + // studioSubmodelRenderInit: R_GeometryRangeAlloc + &g_module_geometry, + + // studioSubmodelRenderInit: R_RenderModelCreate + // studioSubmodelRenderUpdate: R_RenderModelUpdate + // R_StudioDrawPoints: R_RenderModelDraw + // R_StudioRenderFinal: VK_RenderDebugLabelBegin, VK_RenderDebugLabelEnd + // R_StudioDrawModelInternal: VK_RenderDebugLabelBegin, VK_RenderDebugLabelEnd + &g_module_render, + + // buildSubmodelMeshGeometry: R_VkMaterialGetForTextureWithFlags + &g_module_materials, + + // R_StudioLoadTexture: R_TextureUploadFromFile + &g_module_textures_api +}; + +RVkModule g_module_studio = { + .name = "studio", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + // FIXME VK should not be declared here colorVec R_LightVec( const float *start, const float *end, float *lightspot, float *lightvec ); @@ -1577,9 +1612,6 @@ mstudiotexture_t *R_StudioGetTexture( cl_entity_t *e ) return ptexture; } -// TODO where does this need to be declared and defined? currently it's in vk_scene.c -extern int CL_FxBlend( cl_entity_t *e ); - void R_StudioSetRenderamt( int iRenderamt ) { if( !RI.currententity ) return; @@ -3611,8 +3643,23 @@ void CL_InitStudioAPI( void ) pStudioDraw = &gStudioDraw; } -void VK_StudioInit( void ) +void VK_StudioDrawModel( cl_entity_t *ent, int render_mode, float blend ) { + RI.currententity = ent; + RI.currentmodel = ent->model; + RI.drawWorld = true; + + g_studio.blend = blend; + + R_DrawStudioModel( ent ); + + RI.currentmodel = NULL; + RI.currententity = NULL; +} + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_studio ); + Matrix3x4_LoadIdentity( g_studio.rotationmatrix ); // g-cont. cvar disabled by Valve @@ -3627,24 +3674,14 @@ void VK_StudioInit( void ) R_SPEEDS_COUNTER(g_studio_stats.submodels_static, "submodels_static", kSpeedsMetricCount); R_SPEEDS_COUNTER(g_studio_stats.submodels_dynamic, "submodels_dynamic", kSpeedsMetricCount); - VK_StudioModelInit(); + XRVkModule_OnInitEnd( g_module_studio ); + return true; } -void VK_StudioShutdown( void ) -{ - R_StudioCacheClear(); -} +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_studio ); -void VK_StudioDrawModel( cl_entity_t *ent, int render_mode, float blend ) -{ - RI.currententity = ent; - RI.currentmodel = ent->model; - RI.drawWorld = true; - - g_studio.blend = blend; - - R_DrawStudioModel( ent ); + R_StudioCacheClear(); - RI.currentmodel = NULL; - RI.currententity = NULL; + XRVkModule_OnShutdownEnd( g_module_studio ); } diff --git a/ref/vk/vk_studio.h b/ref/vk/vk_studio.h index db16c3e5d..9743c1fa9 100644 --- a/ref/vk/vk_studio.h +++ b/ref/vk/vk_studio.h @@ -1,14 +1,14 @@ #pragma once +#include "vk_module.h" #include "vk_common.h" +extern RVkModule g_module_studio; + struct ref_viewpass_s; struct draw_list_s; struct model_s; -void VK_StudioInit( void ); -void VK_StudioShutdown( void ); - void Mod_StudioLoadTextures( model_t *mod, void *data ); void Mod_StudioUnloadTextures( void *data ); diff --git a/ref/vk/vk_studio_model.c b/ref/vk/vk_studio_model.c index aaddafaea..6dd3bb568 100644 --- a/ref/vk/vk_studio_model.c +++ b/ref/vk/vk_studio_model.c @@ -8,6 +8,25 @@ #define MODULE_NAME "studio" #define LOG_MODULE studio +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // studioRenderSubmodelDestroy: R_GeometryRangeFree + &g_module_geometry, + + // studioRenderSubmodelDestroy: R_RenderModelDestroy + &g_module_render +}; + +RVkModule g_module_studio_model = { + .name = "studio_model", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + typedef struct { const studiohdr_t *studio_header_key; r_studio_model_info_t info; @@ -253,11 +272,6 @@ const r_studio_model_info_t *getStudioModelInfo(model_t *model) { return R_StudioModelPreload(model); } -void VK_StudioModelInit(void) { - R_SPEEDS_METRIC(g_studio_cache.submodels_cached_static, "submodels_cached_static", kSpeedsMetricCount); - R_SPEEDS_METRIC(g_studio_cache.submodels_cached_dynamic, "submodels_cached_dynamic", kSpeedsMetricCount); -} - r_studio_submodel_render_t *studioSubmodelRenderModelAcquire(r_studio_submodel_info_t *subinfo) { const char *mode = ""; @@ -307,3 +321,21 @@ void studioSubmodelRenderModelRelease(r_studio_submodel_render_t *render_submode render_submodel->_.next = render_submodel->_.info->cached_head; render_submodel->_.info->cached_head = render_submodel; } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_studio_model ); + + R_SPEEDS_METRIC(g_studio_cache.submodels_cached_static, "submodels_cached_static", kSpeedsMetricCount); + R_SPEEDS_METRIC(g_studio_cache.submodels_cached_dynamic, "submodels_cached_dynamic", kSpeedsMetricCount); + + XRVkModule_OnInitEnd( g_module_studio_model ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_studio_model ); + + R_StudioCacheClear(); + + XRVkModule_OnShutdownEnd( g_module_studio_model ); +} diff --git a/ref/vk/vk_studio_model.h b/ref/vk/vk_studio_model.h index fbcca76ea..f3366ae71 100644 --- a/ref/vk/vk_studio_model.h +++ b/ref/vk/vk_studio_model.h @@ -1,8 +1,12 @@ #pragma once +#include "vk_module.h" + #include "vk_render.h" #include "vk_geometry.h" +extern RVkModule g_module_studio_model; + struct r_studio_submodel_info_s; // Submodel render data that is enough to render given submodel @@ -63,6 +67,3 @@ typedef struct { int bodyparts_count; r_studio_submodel_render_t **bodyparts; } r_studio_entity_model_t; - -void VK_StudioModelInit(void); -//void VK_StudioModelShutdown(void); diff --git a/ref/vk/vk_swapchain.c b/ref/vk/vk_swapchain.c index ca8a68bc2..056776899 100644 --- a/ref/vk/vk_swapchain.c +++ b/ref/vk/vk_swapchain.c @@ -4,6 +4,23 @@ #include "eiface.h" // ARRAYSIZE +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // createDepthImage: R_VkImageCreate + // destroySwapchainAndFramebuffers: R_VkImageDestroy + &g_module_image +}; + +RVkModule g_module_swapchain = { + .name = "swapchain", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + extern ref_globals_t *gpGlobals; static struct { @@ -181,28 +198,6 @@ static qboolean recreateSwapchainIfNeeded( qboolean force ) { return true; } -qboolean R_VkSwapchainInit( VkRenderPass render_pass, VkFormat depth_format ) { - const uint32_t prev_num_images = g_swapchain.num_images; - - VkSurfaceCapabilitiesKHR surface_caps; - XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &surface_caps)); - -/* -[2023:10:09|13:03:52] Error: Validation: Validation Error: [ VUID-VkSwapchainCreateInfoKHR-imageFormat-01778 ] Object 0: handle = 0x555556af6a00, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xc036022f | vkCreateSwapchainKHR(): pCreateInfo->imageFormat VK_FORMAT_B8G8R8A8_SRGB with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes VK_IMAGE_USAGE_STORAGE_BIT. The Vulkan spec states: The implied image creation parameters of the swapchain must be supported as reported by vkGetPhysicalDeviceImageFormatProperties (https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkSwapchainCreateInfoKHR-imageFormat-01778) -*/ - //g_swapchain.image_format = VK_FORMAT_B8G8R8A8_SRGB; - - g_swapchain.image_format = VK_FORMAT_B8G8R8A8_UNORM; // TODO get from surface_formats - g_swapchain.render_pass = render_pass; - g_swapchain.depth_format = depth_format; - - return recreateSwapchainIfNeeded( true ); -} - -void R_VkSwapchainShutdown( void ) { - destroySwapchainAndFramebuffers( g_swapchain.swapchain ); -} - r_vk_swapchain_framebuffer_t R_VkSwapchainAcquire( VkSemaphore sem_image_available ) { APROF_SCOPE_DECLARE_BEGIN(function, __FUNCTION__); @@ -296,3 +291,45 @@ void R_VkSwapchainPresent( uint32_t index, VkSemaphore done ) { XVK_CHECK(present_result); } } + +void R_VkSwapchainSetRenderPassAndDepthFormat_FIXME( VkRenderPass render_pass, VkFormat depth_format ) { // -- @RenderpassOwnershipMess + g_swapchain.render_pass = render_pass; + g_swapchain.depth_format = depth_format; +} + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_swapchain ); + + const uint32_t prev_num_images = g_swapchain.num_images; + + VkSurfaceCapabilitiesKHR surface_caps; + XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &surface_caps)); + +/* +[2023:10:09|13:03:52] Error: Validation: Validation Error: [ VUID-VkSwapchainCreateInfoKHR-imageFormat-01778 ] Object 0: handle = 0x555556af6a00, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xc036022f | vkCreateSwapchainKHR(): pCreateInfo->imageFormat VK_FORMAT_B8G8R8A8_SRGB with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes VK_IMAGE_USAGE_STORAGE_BIT. The Vulkan spec states: The implied image creation parameters of the swapchain must be supported as reported by vkGetPhysicalDeviceImageFormatProperties (https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkSwapchainCreateInfoKHR-imageFormat-01778) +*/ + //g_swapchain.image_format = VK_FORMAT_B8G8R8A8_SRGB; + + g_swapchain.image_format = VK_FORMAT_B8G8R8A8_UNORM; // TODO get from surface_formats + + // These are set by R_VkSwapchainSetRenderPassAndDepthFormat_FIXME -- @RenderpassOwnershipMess + // g_swapchain.render_pass = render_pass; + // g_swapchain.depth_format = depth_format; + + if ( !recreateSwapchainIfNeeded( true ) ) { + gEngine.Host_Error( S_ERROR "Couldn't create swapchain.\n" ); + g_module_swapchain.state = RVkModuleState_NotInitialized; + return false; + } + + XRVkModule_OnInitEnd( g_module_swapchain ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_swapchain ); + + destroySwapchainAndFramebuffers( g_swapchain.swapchain ); + + XRVkModule_OnShutdownEnd( g_module_swapchain ); +} diff --git a/ref/vk/vk_swapchain.h b/ref/vk/vk_swapchain.h index 84bcadffd..5e9b2e2b4 100644 --- a/ref/vk/vk_swapchain.h +++ b/ref/vk/vk_swapchain.h @@ -1,13 +1,15 @@ #include "vk_core.h" +#include "vk_module.h" // TODO this needs to be negotiated by swapchain creation // however, currently render pass also needs it so ugh #define SWAPCHAIN_FORMAT VK_FORMAT_B8G8R8A8_UNORM //SRGB //#define SWAPCHAIN_FORMAT VK_FORMAT_B8G8R8A8_SRGB +extern RVkModule g_module_swapchain; + // TODO: move render pass and depth format away from this -qboolean R_VkSwapchainInit( VkRenderPass pass, VkFormat depth_format ); -void R_VkSwapchainShutdown( void ); +void R_VkSwapchainSetRenderPassAndDepthFormat_FIXME( VkRenderPass render_pass, VkFormat depth_format ); // -- @RenderpassOwnershipMess typedef struct { uint32_t index; diff --git a/ref/vk/vk_textures.c b/ref/vk/vk_textures.c index 685c301c7..7d5590ba9 100644 --- a/ref/vk/vk_textures.c +++ b/ref/vk/vk_textures.c @@ -24,6 +24,32 @@ #define MAX_SAMPLERS 8 // TF_NEAREST x 2 * TF_BORDER x 2 * TF_CLAMP x 2 +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + // setDescriptorSet: R_TextureGetByIndex + // R_VkTextureGetDescriptorUnorm: R_TextureGetByIndex + // Impl_Init: R_TextureGetByIndex + // &g_module_textures_api, -- @TexturesInitHardcode + + // R_VkTextureDestroy: R_VkStagingFlushSync + &g_module_staging, + + // uploadRawKtx2: R_VkImageCreate, R_VkImageUploadBegin, R_VkImageUploadSlice, R_VkImageUploadEnd + // uploadTexture: R_VkImageCreate, R_VkImageUploadBegin, R_VkImageUploadSlice, R_VkImageUploadEnd + // R_VkTextureDestroy: R_VkImageDestroy + &g_module_image +}; + +RVkModule g_module_textures = { + .name = "textures", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + static struct { struct { int count; @@ -49,14 +75,16 @@ static struct { vk_texture_t blue_noise; } g_vktextures; +static VkSampler pickSamplerForFlags( texFlags_t flags ); +static qboolean uploadTexture( int index, vk_texture_t *tex, const rgbdata_t *pic, colorspace_hint_e colorspace_hint ); +static void textureDestroy( unsigned int index ); +static void loadBlueNoiseTextures( void ); + // Exported from r_textures.h size_t CalcImageSize( pixformat_t format, int width, int height, int depth ); int CalcMipmapCount( int width, int height, int depth, uint32_t flags, qboolean haveBuffer ); void BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, int flags ); -static VkSampler pickSamplerForFlags( texFlags_t flags ); -static qboolean uploadTexture(int index, vk_texture_t *tex, const rgbdata_t *layers, colorspace_hint_e colorspace_hint); - // Hardcode blue noise texture size to 64x64x64 #define BLUE_NOISE_SIZE 64 #define BLUE_NOISE_NAME_F "bluenoise/LDR_RGBA_%d.png" @@ -140,60 +168,6 @@ static void loadBlueNoiseTextures(void) { Mem_Free(scratch); } -qboolean R_VkTexturesInit( void ) { - 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_vktextures.default_sampler = pickSamplerForFlags(0); - ASSERT(g_vktextures.default_sampler != VK_NULL_HANDLE); - - /* FIXME - // validate cvars - R_SetTextureParameters(); - */ - - /* FIXME - gEngine.Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" ); - */ - - // Fill empty texture with references to the default texture - { - const VkImageView default_view = R_TextureGetByIndex(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 = R_TextureGetByIndex(i); - if (tex->vk.image.view) - continue; - - g_vktextures.dii_all_textures[i] = (VkDescriptorImageInfo){ - .imageView = default_view, - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .sampler = g_vktextures.default_sampler, - }; - } - } - - loadBlueNoiseTextures(); - - return true; -} - -static void textureDestroy( unsigned int index ); - -void R_VkTexturesShutdown( void ) { - R_VkTexturesSkyboxUnload(); - R_VkTextureDestroy(-1, &g_vktextures.cubemap_placeholder); - R_VkTextureDestroy(-1, &g_vktextures.blue_noise); - - 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); - } -} - static VkFormat VK_GetFormat(pixformat_t format, colorspace_hint_e colorspace_hint ) { switch(format) { @@ -674,3 +648,85 @@ VkDescriptorImageInfo R_VkTexturesGetBlueNoiseImageInfo( void ) { .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_textures ); + + // NOTE(nilsoncore): We are expecting to be initialized only by main module. See @TexturesInitHardcode + if ( g_module_textures.init_caller != &g_module_textures_api ) { + ERR( "'%s' submodule can only be initialized by '%s'.", g_module_textures.name, g_module_textures_api.name ); + g_module_textures.state = RVkModuleState_NotInitialized; + return false; + } + + 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_vktextures.default_sampler = pickSamplerForFlags(0); + if ( g_vktextures.default_sampler == VK_NULL_HANDLE ) { + ERR( "Couldn't create default sampler." ); + g_module_textures.state = RVkModuleState_NotInitialized; + return false; + } + + /* FIXME + // validate cvars + R_SetTextureParameters(); + */ + + /* FIXME + gEngine.Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" ); + */ + + // Fill empty texture with references to the default texture + { + const VkImageView default_view = R_TextureGetByIndex(tglob.defaultTexture)->vk.image.view; + if ( default_view == VK_NULL_HANDLE ) { + ERR( "Couldn't acquire default image view." ); + g_module_textures.state = RVkModuleState_NotInitialized; + return false; + } + + for (int i = 0; i < MAX_TEXTURES; ++i) { + const vk_texture_t *const tex = R_TextureGetByIndex(i); + if (tex->vk.image.view) + continue; + + g_vktextures.dii_all_textures[i] = (VkDescriptorImageInfo){ + .imageView = default_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .sampler = g_vktextures.default_sampler, + }; + } + } + + if (vk_core.rtx) + loadBlueNoiseTextures(); + + XRVkModule_OnInitEnd( g_module_textures ); + return true; +}; + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_textures ); + + // NOTE(nilsoncore): We are expecting to be shut down only by main module. See @TexturesInitHardcode + if ( g_module_textures.init_caller != &g_module_textures_api ) { + ERR( "'%s' submodule can only be shut down by '%s'.", g_module_textures.name, g_module_textures_api.name ); + } + + R_VkTexturesSkyboxUnload(); + R_VkTextureDestroy(-1, &g_vktextures.cubemap_placeholder); + if (vk_core.rtx) + R_VkTextureDestroy(-1, &g_vktextures.blue_noise); + + 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); + } + + XRVkModule_OnShutdownEnd( g_module_textures ); +}; \ No newline at end of file diff --git a/ref/vk/vk_textures.h b/ref/vk/vk_textures.h index a31e01c39..6aaeba8b1 100644 --- a/ref/vk/vk_textures.h +++ b/ref/vk/vk_textures.h @@ -1,12 +1,16 @@ #pragma once -#include "r_textures.h" #include "vk_core.h" +#include "vk_module.h" + +#include "r_textures.h" #include "vk_image.h" #include "vk_const.h" #include "unordered_roadmap.h" +extern RVkModule g_module_textures; + typedef struct vk_texture_s { urmom_header_t hdr_; @@ -31,9 +35,6 @@ typedef struct vk_texture_s #define TEX_NAME(tex) ((tex)->hdr_.key) -qboolean R_VkTexturesInit( void ); -void R_VkTexturesShutdown( void ); - qboolean R_VkTexturesSkyboxUpload( const char *name, const rgbdata_t *pic, colorspace_hint_e colorspace_hint, qboolean placeholder); void R_VkTexturesSkyboxUnload(void); diff --git a/ref/vk/vk_triapi.c b/ref/vk/vk_triapi.c index 1252fa089..2225a4852 100644 --- a/ref/vk/vk_triapi.c +++ b/ref/vk/vk_triapi.c @@ -9,6 +9,21 @@ #define MAX_TRIAPI_VERTICES 1024 #define MAX_TRIAPI_INDICES 4096 +static qboolean Impl_Init( void ); +static void Impl_Shutdown( void ); + +static RVkModule *required_modules[] = { + &g_module_sprite +}; + +RVkModule g_module_triapi = { + .name = "triapi", + .state = RVkModuleState_NotInitialized, + .dependencies = RVkModuleDependencies_FromStaticArray( required_modules ), + .Init = Impl_Init, + .Shutdown = Impl_Shutdown +}; + static struct { vk_vertex_t vertices[MAX_TRIAPI_VERTICES]; uint16_t indices[MAX_TRIAPI_INDICES]; @@ -18,8 +33,6 @@ static struct { int texture_index; vk_render_type_e render_type; - - qboolean initialized; } g_triapi = {0}; void TriSetTexture( int texture_index ) { @@ -75,11 +88,6 @@ void TriBegin( int primitive_mode ) { if (g_triapi.num_vertices > 1) *ve = g_triapi.vertices[g_triapi.num_vertices-1]; - if (!g_triapi.initialized) { - Vector4Set(ve->color, 255, 255, 255, 255); - g_triapi.initialized = true; - } - g_triapi.primitive_mode = primitive_mode + 1; g_triapi.num_vertices = 0; } @@ -222,3 +230,21 @@ void TriNormal3f( float x, float y, float z ) { vk_vertex_t *const ve = g_triapi.vertices + g_triapi.num_vertices; VectorSet(ve->normal, x, y, z); } + +static qboolean Impl_Init( void ) { + XRVkModule_OnInitStart( g_module_triapi ); + + vk_vertex_t *const ve = g_triapi.vertices + 0; + Vector4Set(ve->color, 255, 255, 255, 255); + + XRVkModule_OnInitEnd( g_module_triapi ); + return true; +} + +static void Impl_Shutdown( void ) { + XRVkModule_OnShutdownStart( g_module_triapi ); + + // Nothing to clear for now. + + XRVkModule_OnShutdownEnd( g_module_triapi ); +} diff --git a/ref/vk/vk_triapi.h b/ref/vk/vk_triapi.h index c3a0f2fd3..dc66359d0 100644 --- a/ref/vk/vk_triapi.h +++ b/ref/vk/vk_triapi.h @@ -1,7 +1,11 @@ #pragma once +#include "vk_module.h" + #include "xash3d_types.h" +extern RVkModule g_module_triapi; + typedef struct model_s model_t; void TriRenderMode( int mode );