diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index a0768598..379ee0ef 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -1,3 +1,10 @@ +# 2023-11-28 E338 +- [x] rendertest + - [x] read imagecompare results + - [x] html report +- [-] rendermode patch + - [ ] track patch by boolean, not another field + # 2023-11-27 E337 - [x] make rendetest.py the central script - [x] parallelize/make gifs @@ -7,7 +14,6 @@ - [ ] consider passing a special flag for single-sided blended surfaces (i.e. brush surfaces) - [x] fix per-entity material mapping, #669 - [x] add to rendertest -- [ ] html report # 2023-11-24 E336 - reproducible rendering: diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index fc0a3299..1ac23b71 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -46,6 +46,7 @@ typedef struct { typedef struct vk_brush_model_s { model_t *engine_model; + int patch_rendermode; r_geometry_range_t geometry; @@ -428,57 +429,6 @@ static vk_render_type_e brushRenderModeToRenderType( int render_mode ) { return kVkRenderTypeSolid; } -#if 0 // TOO OLD -static void brushDrawWaterSurfaces( const cl_entity_t *ent, const vec4_t color, const matrix4x4 transform ) { - const model_t *model = ent->model; - vec3_t mins, maxs; - - if( !VectorIsNull( ent->angles )) - { - for( int i = 0; i < 3; i++ ) - { - mins[i] = ent->origin[i] - model->radius; - maxs[i] = ent->origin[i] + model->radius; - } - //rotated = true; - } - else - { - VectorAdd( ent->origin, model->mins, mins ); - VectorAdd( ent->origin, model->maxs, maxs ); - //rotated = false; - } - - // if( R_CullBox( mins, maxs )) - // return; - - VK_RenderModelDynamicBegin( brushRenderModeToRenderType(ent->curstate.rendermode), color, transform, "%s water", model->name ); - - // Iterate through all surfaces, find *TURB* - for( int i = 0; i < model->nummodelsurfaces; i++ ) - { - const msurface_t *surf = model->surfaces + model->firstmodelsurface + i; - - if( !FBitSet( surf->flags, SURF_DRAWTURB ) && !FBitSet( surf->flags, SURF_DRAWTURB_QUADS) ) - continue; - - if( surf->plane->type != PLANE_Z && !FBitSet( ent->curstate.effects, EF_WATERSIDES )) - continue; - - if( mins[2] + 1.0f >= surf->plane->dist ) - continue; - - EmitWaterPolys( ent, surf, false ); - } - - // submit as dynamic model - VK_RenderModelDynamicCommit(); - - // TODO: - // - upload water geometry only once, animate in compute/vertex shader -} -#endif - typedef struct { const cl_entity_t *ent; const msurface_t *surfaces; @@ -857,6 +807,9 @@ void R_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, con else Matrix4x4_LoadIdentity(transform); + if (bmodel->patch_rendermode >= 0) + render_mode = bmodel->patch_rendermode; + vec4_t color = {1, 1, 1, 1}; vk_render_type_e render_type = kVkRenderTypeSolid; switch (render_mode) { @@ -1042,7 +995,7 @@ static model_sizes_t computeSizes( const model_t *mod, qboolean is_worldmodel ) typedef struct { const model_t *mod; - const vk_brush_model_t *bmodel; + vk_brush_model_t *bmodel; model_sizes_t sizes; uint32_t base_vertex_offset; uint32_t base_index_offset; @@ -1302,8 +1255,14 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { int index_offset = args.base_index_offset; const xvk_mapent_func_any_t *const entity_patch = getModelFuncAnyPatch(args.mod); - if (entity_patch) - DEBUG("Found entity_patch(matmap_count=%d) for model \"%s\"", entity_patch->matmap_count, args.mod->name); + if (entity_patch) { + DEBUG("Found entity_patch(matmap_count=%d, rendermode_patched=%d rendermode=%d) for model \"%s\"", + entity_patch->matmap_count, entity_patch->rendermode_patched, entity_patch->rendermode, args.mod->name); + + if (entity_patch->rendermode_patched > 0) + args.bmodel->patch_rendermode = entity_patch->rendermode; + } + connectVertices(args.mod, entity_patch ? entity_patch->smooth_entire_model : false); // Load sorted by gl_texturenum @@ -1616,6 +1575,7 @@ qboolean R_BrushModelLoad( model_t *mod, qboolean is_worldmodel ) { g_brush.models[g_brush.models_count++] = bmodel; bmodel->engine_model = mod; + bmodel->patch_rendermode = -1; mod->cache.data = bmodel; Matrix4x4_LoadIdentity(bmodel->prev_transform); diff --git a/ref/vk/vk_framectl.c b/ref/vk/vk_framectl.c index f6216e2c..0c419ea4 100644 --- a/ref/vk/vk_framectl.c +++ b/ref/vk/vk_framectl.c @@ -642,7 +642,10 @@ static rgbdata_t *R_VkReadPixels( void ) { 0, 0, NULL, 0, NULL, ARRAYSIZE(image_barrier), image_barrier); } - submit( combuf, true, draw ); + { + const qboolean wait = true; + submit( combuf, wait, draw ); + } // copy bytes to buffer { @@ -705,6 +708,8 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) int width = 0, height = 0; qboolean result; + const uint64_t start_ns = aprof_time_now_ns(); + // get screen frame rgbdata_t *r_shot = R_VkReadPixels(); if (!r_shot) @@ -745,10 +750,15 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) gEngine.Image_Process( &r_shot, width, height, flags, 0.0f ); // write image + const uint64_t save_begin_ns = aprof_time_now_ns(); result = gEngine.FS_SaveImage( filename, r_shot ); + const uint64_t save_end_ns = aprof_time_now_ns(); + gEngine.fsapi->AllowDirectPaths( false ); // always reset after store screenshot gEngine.FS_FreeImage( r_shot ); - gEngine.Con_Printf("Wrote screenshot %s\n", filename); + const uint64_t end_ns = aprof_time_now_ns(); + gEngine.Con_Printf("Wrote screenshot %s. Saving file: %.03fms, total: %.03fms\n", + filename, (save_end_ns - save_begin_ns) / 1e6, (end_ns - start_ns) / 1e6); return result; } diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index b9111440..4bb29332 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -523,10 +523,15 @@ static void patchFuncAnyEntity( const entity_props_t *props, uint32_t have_field if (have_fields & Field_origin) { VectorCopy(props->origin, e->origin); e->origin_patched = true; - DEBUG("Patching ent=%d func_any=%d %f %f %f", e->entity_index, index, e->origin[0], e->origin[1], e->origin[2]); } + if (have_fields & Field_rendermode) { + e->rendermode = props->rendermode; + e->rendermode_patched = true; + DEBUG("Patching ent=%d func_any=%d rendermode=%d", e->entity_index, index, e->rendermode); + } + if (have_fields & Field__xvk_smooth_entire_model) { DEBUG("Patching ent=%d func_any=%d smooth_entire_model =%d", e->entity_index, index, props->_xvk_smooth_entire_model); e->smooth_entire_model = props->_xvk_smooth_entire_model; diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index cc459f8b..0abcd646 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -106,8 +106,6 @@ typedef struct { string model; vec3_t origin; - qboolean origin_patched; - #define MAX_MATERIAL_MAPPINGS 8 int matmap_count; struct { @@ -125,6 +123,10 @@ typedef struct { struct cl_entity_s *ent; */ + + // TODO flags + qboolean origin_patched; + qboolean rendermode_patched; } xvk_mapent_func_any_t; typedef struct { diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index e4a0e7f2..c88d84ca 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -87,6 +87,7 @@ static struct { rt_resource_t res[MAX_RESOURCES]; qboolean reload_pipeline; + qboolean discontinuity; matrix4x4 prev_inv_proj, prev_inv_view; @@ -318,7 +319,7 @@ static void performTracing( vk_combuf_t *combuf, const perform_tracing_args_t* a src->image = tmp_img; // If there was no initial state, prepare it. (this should happen only for the first frame) - if (res->resource.write.pipelines == 0) { + if (g_rtx.discontinuity || res->resource.write.pipelines == 0) { // TODO is there a better way? Can image be cleared w/o explicit clear op? R_VkImageClear( cmdbuf, res->image.image ); res->resource.write.pipelines = VK_PIPELINE_STAGE_TRANSFER_BIT; @@ -656,6 +657,7 @@ 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 ) { @@ -742,3 +744,7 @@ void VK_RayShutdown( void ) { RT_VkAccelShutdown(); RT_DynamicModelShutdown(); } + +void RT_FrameDiscontinuity( void ) { + g_rtx.discontinuity = true; +} diff --git a/ref/vk/vk_rtx.h b/ref/vk/vk_rtx.h index 7bc5dfe0..e49a9289 100644 --- a/ref/vk/vk_rtx.h +++ b/ref/vk/vk_rtx.h @@ -84,3 +84,7 @@ typedef struct { } rt_frame_add_once_t; void RT_FrameAddOnce( rt_frame_add_once_t args ); + +// Signal that the next frame is discontinuous, and all accumulated screen-space +// statistics should be reset. Should help with newmap/saveload/teleport denoiser artifacts. +void RT_FrameDiscontinuity( void ); diff --git a/ref/vk/vk_scene.c b/ref/vk/vk_scene.c index bb544201..9da241d8 100644 --- a/ref/vk/vk_scene.c +++ b/ref/vk/vk_scene.c @@ -252,6 +252,8 @@ void R_NewMap( void ) { // Make sure that EntityData doesn't accidentally reference old pointers. VK_EntityDataClear(); + RT_FrameDiscontinuity(); + // Skip clearing already loaded data if the map hasn't changed. if (is_save_load) return;