From 61178978be96072e33133368414e3f9e7a38bcbf Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Thu, 26 May 2022 22:31:31 +0900 Subject: [PATCH] [vulkan] Get bsp texture animations working again The texture animation data is compacted into a small struct for each texture, resulting in much less data access when animating the texture. More importantly, no looping over the list of frames. I plan on migrating this to at least the other hardware renderers. --- include/QF/Vulkan/qf_bsp.h | 40 ++++++++--- libs/video/renderer/vulkan/vulkan_bsp.c | 93 ++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 17 deletions(-) diff --git a/include/QF/Vulkan/qf_bsp.h b/include/QF/Vulkan/qf_bsp.h index fac47e1cf..376eaa181 100644 --- a/include/QF/Vulkan/qf_bsp.h +++ b/include/QF/Vulkan/qf_bsp.h @@ -64,11 +64,30 @@ typedef struct bsp_packetset_s typedef struct bsp_indexset_s DARRAY_TYPE (uint32_t) bsp_indexset_t; -typedef struct bsp_tex_s { - VkDescriptorSet descriptors; - bsp_packetset_t packets; - bsp_indexset_t indices; -} bsp_tex_t; +typedef struct texname_s { + char name[MIPTEXNAME]; +} texname_t; + +typedef struct texmip_s { + uint32_t width; + uint32_t height; + uint32_t offsets[MIPLEVELS]; +} texmip_t; + +typedef struct texanim_s { + uint16_t base; + byte offset; + byte count; +} texanim_t; + +typedef struct texdata_s { +// texname_t *names; +// texmip_t **mips; + texanim_t *anim_main; + texanim_t *anim_alt; + uint16_t *anim_map; +// int num_tex; +} texdata_t; typedef struct vulktex_s { struct qfv_tex_s *tex; @@ -79,9 +98,6 @@ typedef struct vulktex_s { typedef struct regtexset_s DARRAY_TYPE (vulktex_t *) regtexset_t; -typedef struct bsp_texset_s - DARRAY_TYPE (bsp_tex_t) bsp_texset_t; - typedef struct bsp_draw_s { uint32_t tex_id; uint32_t inst_id; @@ -123,8 +139,9 @@ typedef struct bsp_pass_s { regtexset_t *textures; int num_queues; bsp_drawset_t *draw_queues; - uint32_t inst_id; + uint32_t inst_id; bsp_instance_t *instances; + int ent_frame; } bsp_pass_t; typedef struct bspvert_s { @@ -172,6 +189,8 @@ typedef struct bspctx_s { struct qfv_tex_s *skybox_tex; VkDescriptorSet skybox_descriptor; + vulktex_t notexture; + quat_t default_color; quat_t last_color; @@ -182,6 +201,9 @@ typedef struct bspctx_s { bsp_face_t *faces; uint32_t *poly_indices; + texdata_t texdata; + int anim_index; + int model_id; bsp_pass_t main_pass; // camera view depth, gbuffer, etc diff --git a/libs/video/renderer/vulkan/vulkan_bsp.c b/libs/video/renderer/vulkan/vulkan_bsp.c index 7a83fe7cd..070cb3874 100644 --- a/libs/video/renderer/vulkan/vulkan_bsp.c +++ b/libs/video/renderer/vulkan/vulkan_bsp.c @@ -120,8 +120,14 @@ Vulkan_ClearElements (vulkan_ctx_t *ctx) static inline void chain_surface (const bsp_face_t *face, bsp_pass_t *pass, bspctx_t *bctx) { - - DARRAY_APPEND (&pass->face_queue[face->tex_id], + int ent_frame = pass->ent_frame; + // if the texture has no alt animations, anim_alt holds the sama data + // as anim_main + texanim_t *anim = ent_frame ? &bctx->texdata.anim_alt[face->tex_id] + : &bctx->texdata.anim_main[face->tex_id]; + int anim_ind = (bctx->anim_index + anim->offset) % anim->count; + int tex_id = bctx->texdata.anim_map[anim->base + anim_ind]; + DARRAY_APPEND (&pass->face_queue[tex_id], ((instface_t) { pass->inst_id, face - bctx->faces })); } @@ -157,15 +163,13 @@ clear_textures (vulkan_ctx_t *ctx) void Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) { - int i; - model_t *m; mod_brush_t *brush = &r_refdef.worldmodel->brush; clear_textures (ctx); add_texture (r_notexture_mip, ctx); register_textures (brush, ctx); - for (i = 0; i < num_models; i++) { - m = models[i]; + for (int i = 0; i < num_models; i++) { + model_t *m = models[i]; if (!m) continue; // sub-models are done as part of the main model @@ -181,6 +185,74 @@ Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) bspctx_t *bctx = ctx->bsp_context; int num_tex = bctx->registered_textures.size; + + texture_t **textures = alloca (num_tex * sizeof (texture_t *)); + textures[0] = r_notexture_mip; + for (int i = 0, t = 1; i < num_models; i++) { + model_t *m = models[i]; + // sub-models are done as part of the main model + if (!m || *m->path == '*') { + continue; + } + brush = &m->brush; + for (unsigned j = 0; j < brush->numtextures; j++) { + if (brush->textures[j]) { + textures[t++] = brush->textures[j]; + } + } + } + + size_t texdata_size = 2.5 * num_tex * sizeof (texanim_t); + texanim_t *texdata = Hunk_AllocName (0, texdata_size, "texdata"); + bctx->texdata.anim_main = texdata; + bctx->texdata.anim_alt = texdata + num_tex; + bctx->texdata.anim_map = (uint16_t *) (texdata + 2 * num_tex); + int16_t map_index = 0; + for (int i = 0; i < num_tex; i++) { + texanim_t *anim = bctx->texdata.anim_main + i; + if (anim->count) { + // already done as part of an animation group + continue; + } + *anim = (texanim_t) { .base = map_index, .offset = 0, .count = 1 }; + bctx->texdata.anim_map[anim->base] = i; + + if (textures[i]->anim_total > 1) { + // bsp loader multiplies anim_total by ANIM_CYCLE to slow the + // frame rate + anim->count = textures[i]->anim_total / ANIM_CYCLE; + texture_t *tx = textures[i]->anim_next; + for (int j = 1; j < anim->count; j++) { + if (!tx) { + Sys_Error ("broken cycle"); + } + vulktex_t *vtex = tx->render; + texanim_t *a = bctx->texdata.anim_main + vtex->tex_id; + if (a->count) { + Sys_Error ("crossed cycle"); + } + *a = *anim; + a->offset = j; + bctx->texdata.anim_map[a->base + a->offset] = vtex->tex_id; + tx = tx->anim_next; + } + if (tx != textures[i]) { + Sys_Error ("infinite cycle"); + } + }; + map_index += bctx->texdata.anim_main[i].count; + } + for (int i = 0; i < num_tex; i++) { + texanim_t *alt = bctx->texdata.anim_alt + i; + if (textures[i]->alternate_anims) { + texture_t *tx = textures[i]->alternate_anims; + vulktex_t *vtex = tx->render; + *alt = bctx->texdata.anim_main[vtex->tex_id]; + } else { + *alt = bctx->texdata.anim_main[i]; + } + } + bctx->main_pass.face_queue = malloc (num_tex * sizeof (bsp_instfaceset_t)); for (int i = 0; i < num_tex; i++) { bctx->main_pass.face_queue[i] @@ -398,7 +470,8 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx) bsp_model_t *m = &bctx->models[models[i]->render_id]; m->first_face = face_base + models[i]->brush.firstmodelsurface; m->face_count = models[i]->brush.nummodelsurfaces; - while (models[i + 1] && models[i + 1]->path[0] == '*') { + while (i < num_models - 1 && models[i + 1] + && models[i + 1]->path[0] == '*') { i++; m = &bctx->models[models[i]->render_id]; m->first_face = face_base + models[i]->brush.firstmodelsurface; @@ -529,6 +602,7 @@ R_DrawBrushModel (entity_t *e, bsp_pass_t *pass, vulkan_ctx_t *ctx) return 0; } + pass->ent_frame = e->animation.frame & 1; pass->inst_id = model->render_id; if (!pass->instances[model->render_id].entities.size) { bsp_model_t *m = &bctx->models[model->render_id]; @@ -902,7 +976,7 @@ queue_faces (bsp_pass_t *pass, bspctx_t *bctx, bspframe_t *bframe) size_t dq_size = pass->draw_queues[dq].size; bsp_draw_t *draw = &pass->draw_queues[dq].a[dq_size - 1]; if (!pass->draw_queues[dq].size - || draw->tex_id != f.tex_id + || draw->tex_id != i || draw->inst_id != is.inst_id) { bsp_instance_t *instance = &pass->instances[is.inst_id]; DARRAY_APPEND (&pass->draw_queues[dq], ((bsp_draw_t) { @@ -967,6 +1041,8 @@ Vulkan_DrawWorld (qfv_renderframe_t *rFrame) bctx->main_pass.entid_data = bframe->entid_data; bctx->main_pass.entid_count = 0; + bctx->anim_index = r_data->realtime * 5; + clear_queues (bctx, &bctx->main_pass); // do this first for water and skys bframe->index_count = 0; @@ -981,6 +1057,7 @@ Vulkan_DrawWorld (qfv_renderframe_t *rFrame) Vulkan_Scene_AddEntity (ctx, &worldent); int world_id = worldent.renderer.model->render_id; + bctx->main_pass.ent_frame = 0; // world is always frame 0 bctx->main_pass.inst_id = world_id; DARRAY_APPEND (&bctx->main_pass.instances[world_id].entities, worldent.renderer.render_id);